This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

title: “R Notebook” output: html_document: df_print: paged html_notebook: default pdf_document: default —

This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Customer Lifetime Value Prediction

Problem Statement:

To predict the Customer Lifetime Value for an insurance company offering vehicle insurance.

Description:

Customer Lifetime Value is a commonly used metric by companies and financial institutions to assign a numeric value to their customers and thereby inform their strategy of increasing the companies profits.

It is defined as the total monetary value that a customer holds to a bank or any financial entity over the entire course of their relationship.

The formula used to calculate CLV is = (Annual revenue per customer x Customer relationship in years) – Customer acquisition cost

The difficulty arises when some segments of customers invest a lot of money in a company over a short period of time while others might invest small sums over a longer period of time. Now, if a company were to focus only on the short-term high paying customers, they will miss out on the gradual but constant revenue invested by the latter kind of customer. Both of these kinds of customers might be of high value to the company and hence there is a need to account for these two kinds of customers as well as other factors.

In the case of insurance, customers fall into several categories. Companies design different policies as not all categories of customers will want the same policy. Some customers might go for a greater coverage, while some might go for less. This does not mean that the customers with lesser coverage are less valuable to the company, as we must take into account the cost of acquiring these customers as well.

The insurance company must therefore study their existing customers considering all these factors to find out which category of customers to target.

The dataset contains historical data of the customers already acquired by the company and the CLV for each of these customers has been computed. We must use this previously computed CLV along with the independent variables to predict the category of customers who will be profitable to the company.

To account for all these factors this metric of customer-oriented evaluation is widely used.

Aim:

To establish the relationship between the explanatory variables and the target variable and thereby to propose a model that can predict the target variable.

In this case, the objective is to study how the outcome variable (CLV) is related to the independent variables and the subsequent model thus proposed should help the company to make an informed decision with regard to the kind of customer to target.

It is a regression task to predict how much a given customer will be valuable to an insurance company.

Exploratory Data Analysis

Following are the packages used.

library(tidyverse) 
library(car) 
library(zoo)
library(lmtest) 
library(dplyr) 
library(stringr)
library(caret)
library(ggplot2) 
library(timeDate)
library(plotly)
library(readxl)
library(gganimate)
library(corrplot)
library(Hmisc)
library(vtree)
library(DataExplorer)
library(caTools)
library(nortest)
library(modelr)

Reading the dataset


Marketing_Customer_Value_Analysis_2 <- read_excel('C:/Users/HP/Downloads/Marketing-Customer-Value-Analysis 2.xlsx')

Setting seed for reproducibility and overview of dataset

set.seed(223)
head(Marketing_Customer_Value_Analysis_2)
NA
NA

Histogram of the target variable (CLV)

This shows us the distribution of the target variable, where y-axis contains the probability density of the target variable.

This tells us the monetary value that the customers represent to the company.

Insurance_Dataset <- data.frame(Marketing_Customer_Value_Analysis_2)
hist(Insurance_Dataset$Customer.Lifetime.Value,
     breaks = 800,
     freq = FALSE,
     main = "Histogram of CLV", xlab = "CLV", border = "Blue")

This plot indicates that the distribution is heavily positively skewed, meaning that an overwhelming majority of the customers hold lower customer lifetime value to the company.A very small number of customers are in the higher bracket of lifetime value.

The “ideal” customers to the company are small in number and if the company is to turn a profit they must also focus on catering to the customers with lower CLV as they are more in number.

Description of dataset
Insurance_Dataset %>% introduce()
Insurance_Dataset %>% plot_intro()

There are no null values in this dataset.

CATEGORICAL VARIABLES VISUALIZATION

To visualize the effect of state on CLV


ggplot(Insurance_Dataset,aes (x=State ,
              y=Customer.Lifetime.Value)) + geom_bar(stat="summary",fun="sum", width=0.5, fill = "Blue")+
  labs(x="State",y = "Customer Life Time Value", fill="State") +
  ggtitle("Sum of CLV contribution by State ")

In this case, we’re looking at how much effect a customer’s state has on CLV. In other words, we are trying to find if a customer from a particular state is more valuable to the company than other states.

From the above chart it would appear that the company should focus their efforts on states like California or Oregon, since the sum of CLV from these states are higher. As we can see in the chart below the population of these states is a factor for the high CLV obtained.

count_state <- table(Insurance_Dataset$State)
barplot(count_state, 
        main = "Count plot of State",col = "Blue",
        xlab = "State", ylab = "Count")

Let us explore this by considering the mean of the CLV by state in the subsequent chart. This measure will account for the larger populations of states like California and Oregon.

aggData <- aggregate(x = Insurance_Dataset$Customer.Lifetime.Value,
                     by=list(State = Insurance_Dataset$State),
                     FUN = mean)
ggplot(data = aggData, aes(x = State, y = prop.table(stat(aggData$x)), fill = State, label = scales::percent(prop.table(stat(aggData$x))))) +
  geom_bar(stat="identity", position = "dodge") + 
  geom_text(stat = 'identity', position = position_dodge(.9),  vjust = -0.5, size = 3) + 
  scale_y_continuous(labels = scales::percent) + 
  labs(x = 'State', y = 'CLV in Percentage', fill = 'State') + 
  ggtitle("Mean contribution to CLV by State")

When the mean of the CLV is computed we can see that no particular state is any more economically valuable than the other, as the customers from each state on an average contribute equally to the target variable(CLV). This tells us that state is a weak indicator variable for the CLV.

To visualize the effect of Education on CLV


ggplot(Insurance_Dataset,aes (x=Education ,
              y=Customer.Lifetime.Value)) + geom_bar(stat="summary",fun="sum", width=0.5, fill = "Blue")+
  labs(x="Education",y = "Customer Life Time Value", fill="Education") +
  ggtitle("Contribution of CLV by Education")

count_education <- table(Insurance_Dataset$Education)
barplot(count_education, 
        main = "Count plot of Education",col = "Blue",
        xlab = "Education", ylab = "Count")

aggData <- aggregate(x = Insurance_Dataset$Customer.Lifetime.Value,
                     by=list(Education = Insurance_Dataset$Education),
                     FUN = mean)
ggplot(data = aggData, aes(x = Education, y = prop.table(stat(aggData$x)), fill = Education, label = scales::percent(prop.table(stat(aggData$x))))) +
  geom_bar(stat="identity", position = "dodge") + 
  geom_text(stat = 'identity', position = position_dodge(.9),  vjust = -0.5, size = 3) + 
  scale_y_continuous(labels = scales::percent) + 
  labs(x = 'Education', y = 'CLV in Percentage', fill = 'Education') + 
  ggtitle("Contribution to CLV by Education")

In the first plot it appears as though the contribution of customers having doctors and Master’s qualification is much lesser compared to the customers with other qualifications, which is counter-intuitive. But if we factor in the count of customers from different levels of education, we notice that since there are more customers having Bachelor’s, College and High level qualification, the contribution from these categories is more, as shown in the second chart. This point is further supported by the next chart where the average contribution of each class of qualification is almost the same, which indicates that the value of insurance policies purchased by the customers having doctors and Master’s qualification is much higher than the customers having other qualifications.

To visualize the effect of Coverage on CLV

ggplot(Insurance_Dataset,aes (x=Coverage ,
              y=Customer.Lifetime.Value)) + geom_bar(stat="summary",fun="sum", width=0.5, fill = "Blue")+
  labs(x="Coverage",y = "Customer Life Time Value", fill="Coverage") +
  ggtitle("Contribution to CLV by Coverage")

count_coverage <- table(Insurance_Dataset$Coverage)
barplot(count_coverage, 
        main = "Count plot of Coverage",col = "Blue",
        xlab = "Coverage", ylab = "Count")

aggData <- aggregate(x = Insurance_Dataset$Customer.Lifetime.Value,
                     by=list(Coverage = Insurance_Dataset$Coverage),
                     FUN = mean)
ggplot(data = aggData, aes(x = Coverage, y = prop.table(stat(aggData$x)), fill = Coverage, label = scales::percent(prop.table(stat(aggData$x))))) +
  geom_bar(stat="identity", position = "dodge") + 
  geom_text(stat = 'identity', position = position_dodge(.9),  vjust = -0.5, size = 3) + 
  scale_y_continuous(labels = scales::percent) + 
  labs(x = 'Coverage', y = 'CLV in Percentage', fill = 'Coverage') + 
  ggtitle(" Mean CLV Contribution by Coverage")

It would be apparent from the first chart that the Basic coverage plan has the most contribution to CLV because there are more takers for the basic coverage plan, as proven by the second chart. In the third chart, however, we can see that even though premium coverage plans accounted for the least volume of CLV, on an average a customer having the premium coverage has a greater contribution to CLV.

To visualize the effect of Employment Status on CLV

ggplot(Insurance_Dataset,aes (x=EmploymentStatus ,
              y=Customer.Lifetime.Value)) + geom_bar(stat="summary",fun="sum", width=0.5, fill = "Blue")+
  labs(x="Employment Status",y = "Customer Life Time Value", fill="Employment Status") +
  ggtitle("Contribution to CLV by Employment Status")

count_employmentstatus <- table(Insurance_Dataset$EmploymentStatus)
barplot(count_employmentstatus, 
        main = "Count plot of Employment Status",col = "Blue",
        xlab = "Employment Status", ylab = "Count")

aggData <- aggregate(x = Insurance_Dataset$Customer.Lifetime.Value,
                     by=list(EmploymentStatus = Insurance_Dataset$EmploymentStatus),
                     FUN = mean)
ggplot(data = aggData, aes(x = EmploymentStatus, y = prop.table(stat(aggData$x)), fill = EmploymentStatus, label = scales::percent(prop.table(stat(aggData$x))))) +
  geom_bar(stat="identity", position = "dodge") + 
  geom_text(stat = 'identity', position = position_dodge(.9),  vjust = -0.5, size = 3) + 
  scale_y_continuous(labels = scales::percent) + 
  labs(x = 'Employment Status', y = 'CLV in Percentage', fill = 'Employment Status') + 
  ggtitle("Contribution to CLV by Employment Status")

In the first chart, it is evident that the customers who are employed are of greater value to the company than the other categories. The inference drawn from this is straightforward, i.e employed customers are more likely to be able to afford the premiums and therefore contribute a major chunk to the CLV.

But in the second chart, when we account for the contribution on an average by the employment status, we notice that all are equally contributing to CLV. The reason why the the employed status has such a high contribution to the CLV is because the number of customers who are employed is high.

To visualize the effect of Location Code on CLV.

ggplot(Insurance_Dataset,aes (x=Location.Code ,
              y=Customer.Lifetime.Value)) + geom_bar(stat="summary",fun="sum", width=0.5, fill = "Blue")+
  labs(x="Location Code",y = "Customer Life Time Value", fill="Location Code") +
  ggtitle("Contribution to CLV by Location Code")

count_locationcode <- table(Insurance_Dataset$Location.Code)
barplot(count_locationcode, 
        main = "Count plot of Location Code",col = "Blue",
        xlab = "Location Code", ylab = "Count")

aggData <- aggregate(x = Insurance_Dataset$Customer.Lifetime.Value,
                     by=list(Location.Code = Insurance_Dataset$Location.Code),
                     FUN = mean)
ggplot(data = aggData, aes(x = Location.Code, y = prop.table(stat(aggData$x)), fill = Location.Code, label = scales::percent(prop.table(stat(aggData$x))))) +
  geom_bar(stat="identity", position = "dodge") + 
  geom_text(stat = 'identity', position = position_dodge(.9),  vjust = -0.5, size = 3) + 
  scale_y_continuous(labels = scales::percent) + 
  labs(x = 'Location Code', y = 'CLV in Percentage', fill = 'Location Code') + 
  ggtitle("Contribution to CLV by Location Code")

In the first chart it appears as though customers from the suburban location are a better contributor to CLV than the other areas. From the second chart it is clear that it is because of the higher number of subscribers from suburban areas.

But from the third, we see that all of the location codes on an average contribute equally to the CLV and therefore Location Code is a weak predictor of the CLV on its own.

Effect on CLV by State and Location Code

p1<-plot_ly(Insurance_Dataset, x =~State, y =~Insurance_Dataset$`Customer.Lifetime.Value`,type='bar',color=~Insurance_Dataset$`Location.Code`) 
layout(p1, title ='CLV w.r.t State and Location Code', yaxis = list(title = 'CLV '))
NA

California and Oregon outperform the other states in every location code with regard to CLV.

To visualize the effect of Marital Status on CLV

ggplot(Insurance_Dataset,aes (x=Insurance_Dataset$"Marital.Status", y=Insurance_Dataset$"Customer.Lifetime.Value")) + geom_bar(stat="summary",fun="sum", width=0.5, fill = "Blue") +
  labs(x="Marital Status",y = "Customer Life Time Value", fill="Marital Status") + 
  ggtitle("Visualization of CLV wrt Marital Status")

NA
count_maritalstatus <- table(Insurance_Dataset$Marital.Status)
barplot(count_maritalstatus, 
        main = "Count plot of Marital Status",col = "Blue",
        xlab = "Marital Status", ylab = "Count")

aggData <- aggregate(x = Insurance_Dataset$Customer.Lifetime.Value,
                     by=list(Marital.Status = Insurance_Dataset$Marital.Status),
                     FUN = mean)
ggplot(data = aggData, aes(x = Marital.Status, y = prop.table(stat(aggData$x)), fill = Marital.Status, label = scales::percent(prop.table(stat(aggData$x))))) +
  geom_bar(stat="identity", position = "dodge") + 
  geom_text(stat = 'identity', position = position_dodge(.9),  vjust = -0.5, size = 3) + 
  scale_y_continuous(labels = scales::percent) + 
  labs(x = 'MaritalStatus', y = 'CLV in Percentage', fill = 'Marital Status') + 
  ggtitle("Contribution to CLV by Marital Status")

We might erroneously conclude from the first and the second chart that most married customers have high CLV but the third chart shows us that on an average there is no difference between the contributions of each sub-category to the CLV.

To visualize the effect of Policy Type on CLV

ggplot(Insurance_Dataset,aes (x=Policy.Type ,
              y=Customer.Lifetime.Value)) + geom_bar(stat="summary",fun="sum", width=0.5, fill = "Blue")+
  labs(x="Policy Type",y = "Customer Life Time Value", fill="Policy Type") +
  ggtitle("Contribution to CLV by Policy Type")

ggplot(Insurance_Dataset,aes (x=Policy.Type)) + 
         geom_bar(stat="count", width=0.5, fill = "Blue") +
  labs(x="Policy Type",y = "Count", fill="Policy Type") +
  ggtitle("Count of Policy Type")

aggData <- aggregate(x = Insurance_Dataset$Customer.Lifetime.Value,
                     by=list(Policy.Type = Insurance_Dataset$Policy.Type),
                     FUN = mean)
ggplot(data = aggData, aes(x = Policy.Type, y = prop.table(stat(aggData$x)), fill = Policy.Type, label = scales::percent(prop.table(stat(aggData$x))))) +
  geom_bar(stat="identity", position = "dodge") + 
  geom_text(stat = 'identity', position = position_dodge(.9),  vjust = -0.5, size = 3) + 
  scale_y_continuous(labels = scales::percent) + 
  labs(x = 'Policy Type', y = 'CLV in Percentage', fill = 'Policy Type') + 
  ggtitle("Mean CLV contribution  by Policy Type")

Similar results are obtained as before. Initially it may appear that the personal auto policy might be a majority contributor but further analysis shows that it seems more likely that customers who have purchased the Special Auto have a greater CLV.

To visualize the effect of gender on CLV

ggplot(Insurance_Dataset,aes (x=Gender)) + 
         geom_bar(stat="count", width=0.5, fill = "Blue") +
  labs(x="Gender",y = "Count") +
  ggtitle("Count of Gender")

ggplot(Insurance_Dataset,aes (x=Gender ,
              y=Customer.Lifetime.Value)) + geom_bar(stat="summary",fun="sum", width=0.5, fill = "Blue")+
  labs(x="Gender",y = "Customer Life Time Value", fill="Gender") +
  ggtitle("Contribution to CLV by Gender")

ggplot(Insurance_Dataset,aes (x=Gender ,
              y=Customer.Lifetime.Value)) + geom_bar(stat="summary",fun="mean", width=0.5, fill = "Blue")+
  labs(x="Gender",y = "Customer Life Time Value", fill="Gender") +
  ggtitle("Mean Contribution to CLV by Gender")

Females are on an average slightly better contributors to CLV than men as there are more female subscribers.

To visualize the effect of Sales Channel on CLV.

ggplot(Insurance_Dataset,aes (x=Sales.Channel ,
              y=Customer.Lifetime.Value)) + geom_bar(stat="summary",fun="sum", width=0.5, fill = "Blue")+
  labs(x="Sales Channel",y = "Customer Life Time Value", fill="Sales Channel") +
  ggtitle("Contribution to CLV by Sales Channel")

ggplot(Insurance_Dataset,aes (x=Sales.Channel)) + 
         geom_bar(stat="count", width=0.5, fill = "Blue") +
  labs(x="Policy Type",y = "Count") +
  ggtitle("Count of Sales Channel")

aggData <- aggregate(x = Insurance_Dataset$Customer.Lifetime.Value,
                     by=list(Sales.Channel = Insurance_Dataset$Sales.Channel),
                     FUN = mean)
ggplot(data = aggData, aes(x = Sales.Channel, y = prop.table(stat(aggData$x)), fill = Sales.Channel, label = scales::percent(prop.table(stat(aggData$x))))) +
  geom_bar(stat="identity", position = "dodge") + 
  geom_text(stat = 'identity', position = position_dodge(.9),  vjust = -0.5, size = 3) + 
  scale_y_continuous(labels = scales::percent) + 
  labs(x = 'Sales Channel', y = 'CLV in Percentage', fill = 'Sales Channel') + 
  ggtitle("CLV Distribution by Sales Channel")

The customers procured through agents are contributing to higher CLV.

From the third chart, it is evident that it is hard to predict CLV from Sales Channel as all the sub-categories are equal contributors on an average to CLV. Therefore the insurance company needs to promote the channel which costs the least to sustain operations.

To visualize the effect of Vehicle Class on CLV

ggplot(Insurance_Dataset,aes (x=Vehicle.Class ,
              y=Customer.Lifetime.Value)) + geom_bar(stat="summary",fun="sum", width=0.5, fill = "Blue")+
  labs(x="Vehicle Class",y = "Customer Life Time Value", fill="Vehicle Class") +
  ggtitle("Contribution to CLV by Vehicle Class")

ggplot(Insurance_Dataset,aes (x=Vehicle.Class)) + 
         geom_bar(stat="count", width=0.5, fill = "Blue") +
  labs(x="Vehicle Class",y = "Count") +
  ggtitle("Count of Vehicle Class")

aggData <- aggregate(x = Insurance_Dataset$Customer.Lifetime.Value,
                     by=list(Vehicle.Class = Insurance_Dataset$Vehicle.Class),
                     FUN = mean)
ggplot(data = aggData, aes(x = Vehicle.Class, y = prop.table(stat(aggData$x)), fill = Vehicle.Class, label = scales::percent(prop.table(stat(aggData$x))))) +
  geom_bar(stat="identity", position = "dodge") + 
  geom_text(stat = 'identity', position = position_dodge(.9),  vjust = -0.5, size = 3) + 
  scale_y_continuous(labels = scales::percent) + 
  labs(x = 'Vehicle Class', y = 'CLV in Percentage', fill = 'Vehicle Class') + 
  ggtitle("CLV Distribution by Vehicle Class")

These two charts show us that although the customers owning Luxury, and Luxury SUV are a small fraction, on an average they contribute to almost 50% of CLV. Therefore we can make a conclusion that if a customer owns a Luxury car or Luxury SUV car, there is a high likelihood that she/he she will have high CLV.

Effect on CLV by Marital Status and Vehicle Class

p1<-plot_ly(Insurance_Dataset, x =~Insurance_Dataset$`Marital.Status`, y =~Insurance_Dataset$`Customer.Lifetime.Value`,type='bar',color=~Insurance_Dataset$`Vehicle.Class`) 
layout(p1, title ='CLV status w.r.t Marital Staus  and Vehicle Class', yaxis = list(title = 'CLV '))
NA
NA

When we consider Marital Status and Vehicle Class we notice that across all the marital statuses the customers owning Four-Door cars and SUVs are better contributors to CLV.

To visualize the effect of Vehicle Size on CLV.

aggData <- aggregate(x = Insurance_Dataset$Customer.Lifetime.Value,
                     by=list(Vehicle.Size = Insurance_Dataset$Vehicle.Size),
                     FUN = sum)
ggplot(data = aggData, aes(x = Vehicle.Size, y = prop.table(stat(aggData$x)), fill = Vehicle.Size, label = scales::percent(prop.table(stat(aggData$x))))) +
  geom_bar(stat="identity", position = "dodge") + 
  geom_text(stat = 'identity', position = position_dodge(.9),  vjust = -0.5, size = 3) + 
  scale_y_continuous(labels = scales::percent) + 
  labs(x = 'Vehicle Size', y = 'CLV in Percentage', fill = 'Vehicle Size') + 
  ggtitle("CLV Distribution by Vehicle Size")

ggplot(Insurance_Dataset,aes (x=Vehicle.Size)) + 
         geom_bar(stat="count", width=0.5, fill = "Blue") +
  labs(x="Vehicle Size",y = "Count") +
  ggtitle("Count of Vehicle Size")

aggData <- aggregate(x = Insurance_Dataset$Customer.Lifetime.Value,
                     by=list(Vehicle.Size = Insurance_Dataset$Vehicle.Size),
                     FUN = mean)
ggplot(data = aggData, aes(x = Vehicle.Size, y = prop.table(stat(aggData$x)), fill = Vehicle.Size, label = scales::percent(prop.table(stat(aggData$x))))) +
  geom_bar(stat="identity", position = "dodge") + 
  geom_text(stat = 'identity', position = position_dodge(.9),  vjust = -0.5, size = 3) + 
  scale_y_continuous(labels = scales::percent) + 
  labs(x = 'Vehicle Size', y = 'CLV in Percentage', fill = 'Vehicle Size') + 
  ggtitle("CLV Distribution by Vehicle Size")

As we can see, the variable vehicle size is a weak predictor because all the sub-categories contribute equally to the CLV on an average.

The above tree shows us the breakdown of Coverage plans by vehicle size.

EDA OF NUMERIC DEPENDENT VARIABLES VS CLV.

To visualize the correlation between the variables

A correlation heat map is plotted for all the numeric variables. This also checks for multi-collinearity between variables.

autoCorr <- Insurance_Dataset[,c(3,10,13:17,22)]
colnames(autoCorr) <- c("Customer Lifetime Value", "Income", "Months Premium Auto", "Months Since Last Claim", "Months Since Policy Inception",
                        "Open Complaints", "Num of Policies", "Total Claim Amt.")
autoCorr <- cor(autoCorr)
# Plot the correlation table
corrplot(autoCorr, method = "color", order = "hclust")

As is evident from the correlation plot, Monthly Premium Auto and Total Claim Amount are moderately correlated while the other variables are weakly correlated. Other than Monthly Premium Auto and Total Claim Amount negligible multicollinearity is seen between the remaining independent variables.

To explore the effect of Income and Total Claim Amount

plot(x=Insurance_Dataset$"Income", y=Insurance_Dataset$"Total.Claim.Amount", col="Blue", cex=1, xlab="Income",
     ylab="Total Claim Amount",main="Scatterplot of Income vs TCA")

We see in this chart that there is no linear positive or negative relationship between variables. This means that they are independent of each other

To visualize the effect of Monthly Premium Auto and Total Claim Amount

plot(x=Insurance_Dataset$"Monthly.Premium.Auto", y=Insurance_Dataset$"Total.Claim.Amount", col="Blue", cex=1, xlab="Monthly Premium Auto",
     ylab="Total Claim Amount",main="Scatterplot of MPA vs TCA")

Here we see the relationship of MPA and TCA, we notice that a few clusters have a positive linear relationship, as evidenced by the upward slopes in the chart.

To visualize the effect of Monthly Premium Auto and CLV

plot(x=Insurance_Dataset$"Monthly.Premium.Auto", y=Insurance_Dataset$"Customer.Lifetime.Value", col="Blue", cex=1, xlab="Monthly Premium Auto",
     ylab="Customer Lifetime Value",main="Scatterplot of MPA vs CLV")

From the scatterplot it is evident that higher the MPA, higher is the CLV.

To visualize the effect of Total Claim Amount on CLV.

plot(x=Insurance_Dataset$"Total.Claim.Amount", y=Insurance_Dataset$"Customer.Lifetime.Value", col="Blue", cex=1, xlab="Total.Claim.Amount", 
     ylab="Customer Lifetime Value", main="Scatterplot of TCA vs CLV")

There is no evidence that there is any linear relationship between Total Claim Amount and CLV as the scatterplot is inconclusive. There is no clear slope either downward or upward in the chart indicating that these two variables are independent of each other.

Feature Engineering, Feature Selection and Model Building

Introduction

Having done the EDA, Feature Engineering, Feature Selection and Model Building was carried out.

Feature Engineering:

  1. Sqrt and Log transformations have been used in trying out various models.

  2. Variations of relationship between “Monthly Premium Auto” and “Number of Policies” were tried out but their effect was redundant.

Feature Selection

Feature selection using Stepwise Regression, Random Forest and ANOVA were carried out. These features were used in the various models that were tried out, with and without transformation. The details are in the report below.

Model Building

1. Random Forest.

One of the models that was chosen was Random Forest. Since the data had a lot of outliers, random forest was selected as it is resilient to outliers. Random Forest algorithm itself is not robust to outliers but the base learner on which it is built - the decision tree, is.The R2 value (in percent) and Adjusted R2 values (in percent) of the RF model for all the various trials with different variables was 96% or higher, as shown in the table below:-

Based on research carried out on the internet for this model, Random Forest is biased towards specific factors (like categorical variables with different levels) because it provided exceptionally high results for each experiment, which does not seem realistic. As a result, it was decided to not go ahead with Random Forest.

  • In trial 2, some of the variables were not included which are Customer, State, Response, Effective to Date, Income, Policy Type and Income_Bin. The selection of these variables was through trial and error and were selected as these were giving better results.

2. Linear Regression.

The second model was Linear Regression. The summary of the various models tried out is as given below :-

SUMMARY : REGRESSION

CONCLUSIONS

Various models were tried out and their performance measures tabulated as given above. The models were modelled without following feature selection methods, initially, and, later, using feature selection methods. A summary of the features selected in various models is as shown in a subsequent section.

Without Feature selection and any feature engineering :-

  1. If outliers are not removed, then the performance is extremely poor, even after removing the least significant features.

  2. If outliers are removed from the indicator variables, then the performance of Adj R2improves to approx. 63%. There is hardly any effect in the Adj R2 value when insignificant variables are removed and even after binning is effected. Removal of outliers results in removal of approx. 8% of the records being removed.

  3. When outliers are removed from the respondent variable, clv, the performance dramatically improves to 93%. There is no change in this figure even after binning. Removal of outliers results in removal of approx. 12% of the records being removed.

  4. When outliers are removed from the respondent variable, clv, there are outliers still available in Total Claim Amount and Monthly Premium Auto. Removal of outliers from these features results in removal of a total of 16.88% of records. The performance, however, varies only from the third decimal point onwards wrt the case in point 3 above, and hence there is marginal improvement in performance with all outliers removed.

  5. However, steps 3 and 4 were only to check the effect of removing outliers from the respondent variable. This is not being followed.

With Feature Selection and/or sqrt/log transformation:

  1. The Adj R2 value remains around 63% irrespective of whether outliers are removed or not and whether binning is done or not.Therefore, we retain all outliers as indicators of variation in the data and do not carry out binning. Instead, we carry out scaling of the data.

  2. When we apply sqrt transformation to the respondent variable the Adj R2 value goes up to 79 – 80% approx.

  3. When we apply log transformation to the respondent variable, we obtain Adj R2 values in the region of 89-90%.

  4. The best values are obtained in the model where all the features are taken and the log transformation is applied to the respondent variable.

  5. “Monthly Premium Auto” was omitted from modelling due to its correlation with “Total Claim Amount”.

Pertinent Take-aways:

  1. Binning has negligible effect on performance.

  2. Transformation of Respondent variable (sqrt/Log) has a significant improvement in performance vis-à-vis the non transformed variants.

  3. Removal of outliers improved performance, but also caused significant loss of data. Hence, outliers were retained.

  4. Removal of least significant features hardly caused an improvement in performance. Hence, feature selection techniques were employed.

  5. Converting “Number of Open Complaints” and “Number of Policies” to factors improved accuracy of the models.

Best Model

The best model was one which used all the features and had the log transformation applied to the respondent variable.

Code for the model with the best performance



df<- read.csv("C:\\Users\\HP\\Downloads\\Marketing-Customer-Value-Analysis.csv")
str(df)
'data.frame':   9134 obs. of  24 variables:
 $ Customer                     : chr  "BU79786" "QZ44356" "AI49188" "WW63253" ...
 $ State                        : chr  "Washington" "Arizona" "Nevada" "California" ...
 $ Customer.Lifetime.Value      : num  2764 6980 12887 7646 2814 ...
 $ Response                     : chr  "No" "No" "No" "No" ...
 $ Coverage                     : chr  "Basic" "Extended" "Premium" "Basic" ...
 $ Education                    : chr  "Bachelor" "Bachelor" "Bachelor" "Bachelor" ...
 $ Effective.To.Date            : chr  "2/24/11" "1/31/11" "2/19/11" "1/20/11" ...
 $ EmploymentStatus             : chr  "Employed" "Unemployed" "Employed" "Unemployed" ...
 $ Gender                       : chr  "F" "F" "F" "M" ...
 $ Income                       : int  56274 0 48767 0 43836 62902 55350 0 14072 28812 ...
 $ Location.Code                : chr  "Suburban" "Suburban" "Suburban" "Suburban" ...
 $ Marital.Status               : chr  "Married" "Single" "Married" "Married" ...
 $ Monthly.Premium.Auto         : int  69 94 108 106 73 69 67 101 71 93 ...
 $ Months.Since.Last.Claim      : int  32 13 18 18 12 14 0 0 13 17 ...
 $ Months.Since.Policy.Inception: int  5 42 38 65 44 94 13 68 3 7 ...
 $ Number.of.Open.Complaints    : int  0 0 0 0 0 0 0 0 0 0 ...
 $ Number.of.Policies           : int  1 8 2 7 1 2 9 4 2 8 ...
 $ Policy.Type                  : chr  "Corporate Auto" "Personal Auto" "Personal Auto" "Corporate Auto" ...
 $ Policy                       : chr  "Corporate L3" "Personal L3" "Personal L3" "Corporate L2" ...
 $ Renew.Offer.Type             : chr  "Offer1" "Offer3" "Offer1" "Offer1" ...
 $ Sales.Channel                : chr  "Agent" "Agent" "Agent" "Call Center" ...
 $ Total.Claim.Amount           : num  385 1131 566 530 138 ...
 $ Vehicle.Class                : chr  "Two-Door Car" "Four-Door Car" "Two-Door Car" "SUV" ...
 $ Vehicle.Size                 : chr  "Medsize" "Medsize" "Medsize" "Medsize" ...
glimpse(df)
Rows: 9,134
Columns: 24
$ Customer                      <chr> "BU79786", "QZ44356", "AI49188", "~
$ State                         <chr> "Washington", "Arizona", "Nevada",~
$ Customer.Lifetime.Value       <dbl> 2763.519, 6979.536, 12887.432, 764~
$ Response                      <chr> "No", "No", "No", "No", "No", "Yes~
$ Coverage                      <chr> "Basic", "Extended", "Premium", "B~
$ Education                     <chr> "Bachelor", "Bachelor", "Bachelor"~
$ Effective.To.Date             <chr> "2/24/11", "1/31/11", "2/19/11", "~
$ EmploymentStatus              <chr> "Employed", "Unemployed", "Employe~
$ Gender                        <chr> "F", "F", "F", "M", "M", "F", "F",~
$ Income                        <int> 56274, 0, 48767, 0, 43836, 62902, ~
$ Location.Code                 <chr> "Suburban", "Suburban", "Suburban"~
$ Marital.Status                <chr> "Married", "Single", "Married", "M~
$ Monthly.Premium.Auto          <int> 69, 94, 108, 106, 73, 69, 67, 101,~
$ Months.Since.Last.Claim       <int> 32, 13, 18, 18, 12, 14, 0, 0, 13, ~
$ Months.Since.Policy.Inception <int> 5, 42, 38, 65, 44, 94, 13, 68, 3, ~
$ Number.of.Open.Complaints     <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0~
$ Number.of.Policies            <int> 1, 8, 2, 7, 1, 2, 9, 4, 2, 8, 3, 3~
$ Policy.Type                   <chr> "Corporate Auto", "Personal Auto",~
$ Policy                        <chr> "Corporate L3", "Personal L3", "Pe~
$ Renew.Offer.Type              <chr> "Offer1", "Offer3", "Offer1", "Off~
$ Sales.Channel                 <chr> "Agent", "Agent", "Agent", "Call C~
$ Total.Claim.Amount            <dbl> 384.81115, 1131.46493, 566.47225, ~
$ Vehicle.Class                 <chr> "Two-Door Car", "Four-Door Car", "~
$ Vehicle.Size                  <chr> "Medsize", "Medsize", "Medsize", "~
#-----------------------------------Min Max normalization all numeric variables (Scaling)---------------------------#

#Income
df$Income<- (df$Income-min(df$Income))/(max(df$Income)-min(df$Income))
#Months.Since.Last.Claim
df$Months.Since.Last.Claim<- (df$Months.Since.Last.Claim-min(df$Months.Since.Last.Claim))/(max(df$Months.Since.Last.Claim)-min(df$Months.Since.Last.Claim))
#Months.Since.Policy.Inception
df$Months.Since.Policy.Inception<- (df$Months.Since.Policy.Inception-min(df$Months.Since.Policy.Inception))/(max(df$Months.Since.Policy.Inception)-min(df$Months.Since.Policy.Inception))
#Total.Claim.Amount
df$Total.Claim.Amount<- (df$Total.Claim.Amount-min(df$Total.Claim.Amount))/(max(df$Total.Claim.Amount)-min(df$Total.Claim.Amount))
#converting categorical feauters to factors.
df$State <- as.factor(df$State)
df$Response <- as.factor(df$Response)
df$Coverage <- as.factor(df$Coverage)
df$Education <- as.factor(df$Education)
df$EmploymentStatus <- as.factor(df$EmploymentStatus)
df$Gender <- as.factor(df$Gender)
df$Location.Code  <- as.factor(df$Location.Code)
df$Marital.Status  <- as.factor(df$Marital.Status)
df$Policy.Type  <- as.factor(df$Policy.Type)
df$Renew.Offer.Type  <- as.factor(df$Renew.Offer.Type)
df$Policy  <- as.factor(df$Policy)
df$Sales.Channel  <- as.factor(df$Sales.Channel)
df$Vehicle.Class  <- as.factor(df$Vehicle.Class)
df$Vehicle.Size  <- as.factor(df$Vehicle.Size)
#Converting no. of open complaints and policies also to factor.
df$Number.of.Open_Complaints <- as.factor(df$Number.of.Open.Complaints)
df$Number.of.Policies <- as.factor(df$Number.of.Policies)
str(df)
'data.frame':   9134 obs. of  25 variables:
 $ Customer                     : chr  "BU79786" "QZ44356" "AI49188" "WW63253" ...
 $ State                        : Factor w/ 5 levels "Arizona","California",..: 5 1 3 2 5 4 4 1 4 4 ...
 $ Customer.Lifetime.Value      : num  2764 6980 12887 7646 2814 ...
 $ Response                     : Factor w/ 2 levels "No","Yes": 1 1 1 1 1 2 2 1 2 1 ...
 $ Coverage                     : Factor w/ 3 levels "Basic","Extended",..: 1 2 3 1 1 1 1 3 1 2 ...
 $ Education                    : Factor w/ 5 levels "Bachelor","College",..: 1 1 1 1 1 1 2 5 1 2 ...
 $ Effective.To.Date            : chr  "2/24/11" "1/31/11" "2/19/11" "1/20/11" ...
 $ EmploymentStatus             : Factor w/ 5 levels "Disabled","Employed",..: 2 5 2 5 2 2 2 5 3 2 ...
 $ Gender                       : Factor w/ 2 levels "F","M": 1 1 1 2 2 1 1 2 2 1 ...
 $ Income                       : num  0.563 0 0.488 0 0.438 ...
 $ Location.Code                : Factor w/ 3 levels "Rural","Suburban",..: 2 2 2 2 1 1 2 3 2 3 ...
 $ Marital.Status               : Factor w/ 3 levels "Divorced","Married",..: 2 3 2 2 3 2 2 3 1 2 ...
 $ Monthly.Premium.Auto         : int  69 94 108 106 73 69 67 101 71 93 ...
 $ Months.Since.Last.Claim      : num  0.914 0.371 0.514 0.514 0.343 ...
 $ Months.Since.Policy.Inception: num  0.0505 0.4242 0.3838 0.6566 0.4444 ...
 $ Number.of.Open.Complaints    : int  0 0 0 0 0 0 0 0 0 0 ...
 $ Number.of.Policies           : Factor w/ 9 levels "1","2","3","4",..: 1 8 2 7 1 2 9 4 2 8 ...
 $ Policy.Type                  : Factor w/ 3 levels "Corporate Auto",..: 1 2 2 1 2 2 1 1 1 3 ...
 $ Policy                       : Factor w/ 9 levels "Corporate L1",..: 3 6 6 2 4 6 3 3 3 8 ...
 $ Renew.Offer.Type             : Factor w/ 4 levels "Offer1","Offer2",..: 1 3 1 1 1 2 1 1 1 2 ...
 $ Sales.Channel                : Factor w/ 4 levels "Agent","Branch",..: 1 1 1 3 1 4 1 1 1 2 ...
 $ Total.Claim.Amount           : num  0.133 0.3911 0.1958 0.1831 0.0477 ...
 $ Vehicle.Class                : Factor w/ 6 levels "Four-Door Car",..: 6 1 6 5 1 6 1 1 1 1 ...
 $ Vehicle.Size                 : Factor w/ 3 levels "Large","Medsize",..: 2 2 2 2 2 2 2 2 2 2 ...
 $ Number.of.Open_Complaints    : Factor w/ 6 levels "0","1","2","3",..: 1 1 1 1 1 1 1 1 1 1 ...
#-----------------------------------Log transformation only on CLV---------------------------#


df$Customer.Lifetime.Value=log(df$Customer.Lifetime.Value)
#-----------------------------------Splitting the data to test and train.---------------------------#


split <- sample.split(df, SplitRatio = 0.7)
split
 [1] FALSE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE FALSE FALSE  TRUE
[12]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE
[23]  TRUE  TRUE  TRUE
train <- subset(df, split="true")
test <-subset(df, split="false")
train
#Training the model with normalized data columns and log transformed clv
fit3<- lm(Customer.Lifetime.Value ~     State+Response+Coverage+
           Education+EmploymentStatus+Gender+
           Income+Location.Code+Marital.Status+
           Months.Since.Last.Claim+Months.Since.Policy.Inception+
           Number.of.Open.Complaints+Number.of.Policies+Policy+            Renew.Offer.Type+Sales.Channel+Total.Claim.Amount+Vehicle.Class+Vehicle.Size , data=train)

summary(fit3)

Call:
lm(formula = Customer.Lifetime.Value ~ State + Response + Coverage + 
    Education + EmploymentStatus + Gender + Income + Location.Code + 
    Marital.Status + Months.Since.Last.Claim + Months.Since.Policy.Inception + 
    Number.of.Open.Complaints + Number.of.Policies + Policy + 
    Renew.Offer.Type + Sales.Channel + Total.Claim.Amount + Vehicle.Class + 
    Vehicle.Size, data = train)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.49334 -0.08354 -0.00878  0.05855  0.95022 

Coefficients:
                                Estimate Std. Error t value Pr(>|t|)    
(Intercept)                    7.809e+00  2.100e-02 371.809  < 2e-16 ***
StateCalifornia                2.396e-03  6.464e-03   0.371  0.71087    
StateNevada                    1.583e-02  8.916e-03   1.776  0.07582 .  
StateOregon                    4.360e-03  6.690e-03   0.652  0.51456    
StateWashington                9.225e-03  9.213e-03   1.001  0.31669    
ResponseYes                   -5.999e-04  7.085e-03  -0.085  0.93253    
CoverageExtended               2.171e-01  5.224e-03  41.550  < 2e-16 ***
CoveragePremium                4.225e-01  8.906e-03  47.437  < 2e-16 ***
EducationCollege              -3.350e-03  5.843e-03  -0.573  0.56642    
EducationDoctor                2.352e-02  1.239e-02   1.899  0.05766 .  
EducationHigh School or Below  1.313e-02  5.920e-03   2.219  0.02654 *  
EducationMaster                2.656e-02  8.972e-03   2.960  0.00308 ** 
EmploymentStatusEmployed       6.501e-02  1.210e-02   5.371 8.04e-08 ***
EmploymentStatusMedical Leave  3.594e-02  1.489e-02   2.414  0.01581 *  
EmploymentStatusRetired       -1.364e-03  1.717e-02  -0.079  0.93666    
EmploymentStatusUnemployed    -1.294e-02  1.221e-02  -1.060  0.28912    
GenderM                       -2.956e-02  4.535e-03  -6.518 7.51e-11 ***
Income                         3.550e-02  1.318e-02   2.694  0.00708 ** 
Location.CodeSuburban         -2.811e-02  8.888e-03  -3.163  0.00157 ** 
Location.CodeUrban            -1.260e-02  8.196e-03  -1.537  0.12429    
Marital.StatusMarried          8.239e-03  6.650e-03   1.239  0.21536    
Marital.StatusSingle          -3.234e-02  7.696e-03  -4.202 2.67e-05 ***
Months.Since.Last.Claim        1.607e-02  7.829e-03   2.053  0.04011 *  
Months.Since.Policy.Inception  3.052e-03  8.042e-03   0.380  0.70430    
Number.of.Open.Complaints     -2.071e-02  2.474e-03  -8.372  < 2e-16 ***
Number.of.Policies2            1.403e+00  5.898e-03 237.848  < 2e-16 ***
Number.of.Policies3            6.955e-01  7.367e-03  94.418  < 2e-16 ***
Number.of.Policies4            6.982e-01  1.131e-02  61.749  < 2e-16 ***
Number.of.Policies5            7.016e-01  1.133e-02  61.898  < 2e-16 ***
Number.of.Policies6            6.923e-01  1.180e-02  58.678  < 2e-16 ***
Number.of.Policies7            6.942e-01  1.104e-02  62.893  < 2e-16 ***
Number.of.Policies8            6.986e-01  1.162e-02  60.142  < 2e-16 ***
Number.of.Policies9            7.029e-01  1.122e-02  62.649  < 2e-16 ***
PolicyCorporate L2            -1.686e-02  1.435e-02  -1.175  0.24008    
PolicyCorporate L3            -5.712e-03  1.318e-02  -0.433  0.66483    
PolicyPersonal L1             -9.331e-03  1.287e-02  -0.725  0.46852    
PolicyPersonal L2             -4.154e-03  1.225e-02  -0.339  0.73448    
PolicyPersonal L3             -6.310e-03  1.191e-02  -0.530  0.59631    
PolicySpecial L1              -2.324e-02  2.879e-02  -0.807  0.41955    
PolicySpecial L2               1.791e-03  2.025e-02   0.088  0.92955    
PolicySpecial L3               3.217e-02  2.097e-02   1.534  0.12503    
Renew.Offer.TypeOffer2         6.280e-03  5.671e-03   1.107  0.26812    
Renew.Offer.TypeOffer3         6.948e-03  6.841e-03   1.016  0.30982    
Renew.Offer.TypeOffer4         2.589e-03  7.988e-03   0.324  0.74585    
Sales.ChannelBranch            8.831e-03  5.625e-03   1.570  0.11646    
Sales.ChannelCall Center       5.967e-03  6.358e-03   0.939  0.34801    
Sales.ChannelWeb               8.507e-06  7.034e-03   0.001  0.99904    
Total.Claim.Amount             2.261e-01  4.544e-02   4.977 6.59e-07 ***
Vehicle.ClassLuxury Car        9.240e-01  2.049e-02  45.098  < 2e-16 ***
Vehicle.ClassLuxury SUV        9.440e-01  1.940e-02  48.667  < 2e-16 ***
Vehicle.ClassSports Car        4.632e-01  1.077e-02  43.030  < 2e-16 ***
Vehicle.ClassSUV               4.374e-01  6.845e-03  63.895  < 2e-16 ***
Vehicle.ClassTwo-Door Car      3.066e-03  5.876e-03   0.522  0.60182    
Vehicle.SizeMedsize            4.447e-03  7.500e-03   0.593  0.55324    
Vehicle.SizeSmall              4.554e-03  8.739e-03   0.521  0.60230    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2142 on 9079 degrees of freedom
Multiple R-squared:  0.893, Adjusted R-squared:  0.8924 
F-statistic:  1403 on 54 and 9079 DF,  p-value: < 2.2e-16
#Finding RSE
sigma(fit3)
[1] 0.214212
# computing test MSE
test %>% 
  add_predictions(fit3) %>%
  summarise(MSE = mean((Customer.Lifetime.Value - pred)^2))
# computing train MSE
train %>% 
  add_predictions(fit3) %>%
  summarise(MSE = mean((Customer.Lifetime.Value - pred)^2))
#Plotting the model.
plot(fit3)

##################################### Checking of Assumption ############################################


# 1. In the residual vs fitted graph we cannot see any funnel shape in the residues, hence the assumption of homoskedasticity is satisfied.

# 2. The points in the center part of the graphs follow the Q-Q plot. The trailing portion deviates from the Q-Q plot by a small amount. However, the leading portion deviates significantly from the Q-Q plot indicating non adherence to normality. Therefore, Log transformation has been applied to the target variable. The graph of the log transformed target variable is displayed below. As we can see graph resembles the normal curve.

# 3. Residuals are spread equally along the ranges of predictors, indicating homoscedasticity. We can see a  horizontal line with equally (randomly) spread points.

# 4. Even though there seems to be extreme values, the regression line is more or less straight.  
# Plot of Log transformed CLV 
hist(df$Customer.Lifetime.Value,
breaks = 800,
freq = FALSE,
main = "CLV Histogram", xlab = "CLV", border = "Blue")



# Residuals should be uncorrelated.There should be no Autocorrelation.
# Null H0: residuals from a linear regression are uncorrelated. 
# D-W Statistic should be close to 2. 


durbinWatsonTest(fit3)
 lag Autocorrelation D-W Statistic p-value
   1     0.008965194      1.982047    0.34
 Alternative hypothesis: rho != 0
#Since, the p-value is >0.05, we fail to reject H0: (No Autocorrelation)
# Checking multicollinearity

vif(fit3)
                                  GVIF Df GVIF^(1/(2*Df))
State                         1.022224  4        1.002751
Response                      1.226011  1        1.107254
Coverage                      1.291001  2        1.065937
Education                     1.089738  4        1.010800
EmploymentStatus              3.918643  4        1.186156
Gender                        1.022844  1        1.011358
Income                        3.190906  1        1.786311
Location.Code                 2.625380  2        1.272911
Marital.Status                1.328824  2        1.073660
Months.Since.Last.Claim       1.010612  1        1.005292
Months.Since.Policy.Inception 1.022789  1        1.011330
Number.of.Open.Complaints     1.009651  1        1.004814
Number.of.Policies            1.085342  8        1.005132
Policy                        1.043708  8        1.002677
Renew.Offer.Type              1.275012  3        1.041324
Sales.Channel                 1.066915  3        1.010854
Total.Claim.Amount            4.143505  1        2.035560
Vehicle.Class                 2.236223  5        1.083806
Vehicle.Size                  1.067929  2        1.016566
# The values of VIF should be within 2. And in no case it should be greater than 10. 
# Since all values are from 1 to 4, absence of multicollinearity is witnessed. 

#  After checking the assumption of the linear regression model we can say that the assumptions seems to be largely satisfied.

Additional Data that could have better predicted the outcome variable

1.In order to better analyze the CLV of a customer, the company needs to make sure the customer stays with them. This can be calculated using “Customer Retention Rate” : Customer Retention Rate is the number of customers retained by a company over a certain time period. It’s expressed as a percentage of a company’s existing customers who remain loyal within that time frame. CRR = [(E-N)/S] x 100 Where : The number of existing customers at the start of the time period (S) The number of total customers at the end of the time period (E) The number of new customers added within the time period (N)

2.If a customer has made no claims for a period of n years, company can provide perks such as “no claim bonus”, which would essentially reduce the premium amount payable at next renewal, while keeping the insurance cover at the same or higher value. We have the data regarding “Months since last claim” , however if we have an additional data regarding customers response to opting for “no claim bonus” it would help us analyze customer preference . Hence, leading to better prediction of customers with consistent CLV

  1. If the cost of sustaining Sales Channel was provided it would have helped us analyze the sustainability cost of each Sales Channel, with which we could have essentially found out which Sales Channel is contributing effectively to the CLV. Based on the outcome, we can suggest courses of action to the company reduce such overheads.

Contribution of team members

To coordinate the team activities, formal meetings were held every day at 1200 hrs and 1900 hrs, besides informal meetings on teams, whatsapp calls, phone calls and innumerable chats at all hours of the day.

Initially everybody carried out EDA. The results of EDA were discussed. Further courses of action on EDA were discussed and carried out. Once EDA was satisfactorily done, feature selection and model building was carried out. Joel, Alex and Tashi carried out feature selection using Stepwise Regression. Sarah, Aishwarya and Bart carried out Feature selection using Random Forest and ANOVA. Sarah, Aishwarya and Bart carried out model building using Random Forest implementing various models with and without transformation as brought out in the report above. Joel, Alex and Tashi constructed various models using Regression, with and without transformation as well as, with and without scaling as brought out in the report above. Retention and deletion of outliers and effect of binning was also experimented with, in the models by both sub-teams.

Finally, having compared the various results and zeroing on the most operative model was done, the report was prepared drawing from the EDA and Model Building done earlier. All members sat together and constructed the report vetting all aspects of the report collectively.

All members were involved at every stage of the process from beginning to end.

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6DQogIHBkZl9kb2N1bWVudDogZGVmYXVsdA0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQotLS0NCg0KVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIFdoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gDQotLS0NCnRpdGxlOiAiUiBOb3RlYm9vayINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBkZl9wcmludDogcGFnZWQNCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdA0KICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KDQpUaGlzIGlzIGFuIFtSIE1hcmtkb3duXShodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tKSBOb3RlYm9vay4gV2hlbiB5b3UgZXhlY3V0ZSBjb2RlIHdpdGhpbiB0aGUgbm90ZWJvb2ssIHRoZSByZXN1bHRzIGFwcGVhciBiZW5lYXRoIHRoZSBjb2RlLiANCg0KIyBDdXN0b21lciBMaWZldGltZSBWYWx1ZSBQcmVkaWN0aW9uDQoNCiMjIyBQcm9ibGVtIFN0YXRlbWVudDoNCg0KVG8gcHJlZGljdCB0aGUgQ3VzdG9tZXIgTGlmZXRpbWUgVmFsdWUgZm9yIGFuIGluc3VyYW5jZSBjb21wYW55IG9mZmVyaW5nIHZlaGljbGUgaW5zdXJhbmNlLg0KDQoNCiMjIyBEZXNjcmlwdGlvbjoNCg0KQ3VzdG9tZXIgTGlmZXRpbWUgVmFsdWUgaXMgYSBjb21tb25seSB1c2VkIG1ldHJpYyBieSBjb21wYW5pZXMgYW5kIGZpbmFuY2lhbCBpbnN0aXR1dGlvbnMgdG8gYXNzaWduIGEgbnVtZXJpYyB2YWx1ZSB0byB0aGVpciBjdXN0b21lcnMgYW5kIHRoZXJlYnkgaW5mb3JtIHRoZWlyIHN0cmF0ZWd5IG9mIGluY3JlYXNpbmcgdGhlIGNvbXBhbmllcyBwcm9maXRzLg0KDQpJdCBpcyBkZWZpbmVkIGFzIHRoZSB0b3RhbCBtb25ldGFyeSB2YWx1ZSB0aGF0IGEgY3VzdG9tZXIgaG9sZHMgdG8gYSBiYW5rIG9yIGFueSBmaW5hbmNpYWwgZW50aXR5IG92ZXIgdGhlIGVudGlyZSBjb3Vyc2Ugb2YgdGhlaXIgcmVsYXRpb25zaGlwLg0KDQoNCg0KDQoNCiMjIyMgVGhlIGZvcm11bGEgdXNlZCB0byBjYWxjdWxhdGUgQ0xWIGlzID0gKEFubnVhbCByZXZlbnVlIHBlciBjdXN0b21lciB4IEN1c3RvbWVyIHJlbGF0aW9uc2hpcCBpbiB5ZWFycykg4oCTIEN1c3RvbWVyIGFjcXVpc2l0aW9uIGNvc3QNCg0KDQoNCg0KDQpUaGUgZGlmZmljdWx0eSBhcmlzZXMgd2hlbiBzb21lIHNlZ21lbnRzIG9mIGN1c3RvbWVycyBpbnZlc3QgYSBsb3Qgb2YgbW9uZXkgaW4gYSBjb21wYW55IG92ZXIgYSBzaG9ydCBwZXJpb2Qgb2YgdGltZSB3aGlsZSBvdGhlcnMgbWlnaHQgaW52ZXN0IHNtYWxsIHN1bXMgb3ZlciBhIGxvbmdlciBwZXJpb2Qgb2YgdGltZS4gTm93LCBpZiBhIGNvbXBhbnkgd2VyZSB0byBmb2N1cyBvbmx5IG9uIHRoZSBzaG9ydC10ZXJtIGhpZ2ggcGF5aW5nIGN1c3RvbWVycywgdGhleSB3aWxsIG1pc3Mgb3V0IG9uIHRoZSBncmFkdWFsIGJ1dCBjb25zdGFudCByZXZlbnVlIGludmVzdGVkIGJ5IHRoZSBsYXR0ZXIga2luZCBvZiBjdXN0b21lci4gQm90aCBvZiB0aGVzZSBraW5kcyBvZiBjdXN0b21lcnMgbWlnaHQgYmUgb2YgaGlnaCB2YWx1ZSB0byB0aGUgY29tcGFueSBhbmQgaGVuY2UgdGhlcmUgaXMgYSBuZWVkIHRvIGFjY291bnQgZm9yIHRoZXNlIHR3byBraW5kcyBvZiBjdXN0b21lcnMgYXMgd2VsbCBhcyBvdGhlciBmYWN0b3JzLiANCg0KDQpJbiB0aGUgY2FzZSBvZiBpbnN1cmFuY2UsIGN1c3RvbWVycyBmYWxsIGludG8gc2V2ZXJhbCBjYXRlZ29yaWVzLiBDb21wYW5pZXMgZGVzaWduIGRpZmZlcmVudCBwb2xpY2llcyBhcyBub3QgYWxsIGNhdGVnb3JpZXMgb2YgY3VzdG9tZXJzIHdpbGwgd2FudCB0aGUgc2FtZSBwb2xpY3kuIFNvbWUgY3VzdG9tZXJzIG1pZ2h0IGdvIGZvciBhIGdyZWF0ZXIgY292ZXJhZ2UsIHdoaWxlIHNvbWUgbWlnaHQgZ28gZm9yIGxlc3MuIFRoaXMgZG9lcyBub3QgbWVhbiB0aGF0IHRoZSBjdXN0b21lcnMgd2l0aCBsZXNzZXIgY292ZXJhZ2UgYXJlIGxlc3MgdmFsdWFibGUgdG8gdGhlIGNvbXBhbnksIGFzIHdlIG11c3QgdGFrZSBpbnRvIGFjY291bnQgdGhlIGNvc3Qgb2YgYWNxdWlyaW5nIHRoZXNlIGN1c3RvbWVycyBhcyB3ZWxsLg0KDQpUaGUgaW5zdXJhbmNlIGNvbXBhbnkgbXVzdCB0aGVyZWZvcmUgc3R1ZHkgdGhlaXIgZXhpc3RpbmcgY3VzdG9tZXJzIGNvbnNpZGVyaW5nIGFsbCB0aGVzZSBmYWN0b3JzIHRvIGZpbmQgb3V0IHdoaWNoIGNhdGVnb3J5IG9mIGN1c3RvbWVycyB0byB0YXJnZXQuDQoNClRoZSBkYXRhc2V0IGNvbnRhaW5zIGhpc3RvcmljYWwgZGF0YSBvZiB0aGUgY3VzdG9tZXJzIGFscmVhZHkgYWNxdWlyZWQgYnkgdGhlIGNvbXBhbnkgYW5kIHRoZSBDTFYgZm9yIGVhY2ggb2YgdGhlc2UgY3VzdG9tZXJzIGhhcyBiZWVuIGNvbXB1dGVkLiBXZSBtdXN0IHVzZSB0aGlzIHByZXZpb3VzbHkgY29tcHV0ZWQgQ0xWIGFsb25nIHdpdGggdGhlIGluZGVwZW5kZW50IHZhcmlhYmxlcyB0byBwcmVkaWN0IHRoZSBjYXRlZ29yeSBvZiBjdXN0b21lcnMgd2hvIHdpbGwgYmUgcHJvZml0YWJsZSB0byB0aGUgY29tcGFueS4NCg0KVG8gYWNjb3VudCBmb3IgYWxsIHRoZXNlIGZhY3RvcnMgdGhpcyBtZXRyaWMgb2YgY3VzdG9tZXItb3JpZW50ZWQgZXZhbHVhdGlvbiBpcyB3aWRlbHkgdXNlZC4NCg0KDQojIyMgQWltOg0KDQpUbyBlc3RhYmxpc2ggdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBleHBsYW5hdG9yeSB2YXJpYWJsZXMgYW5kIHRoZSB0YXJnZXQgdmFyaWFibGUgYW5kIHRoZXJlYnkgdG8gcHJvcG9zZSBhIG1vZGVsIHRoYXQgY2FuIHByZWRpY3QgdGhlIHRhcmdldCB2YXJpYWJsZS4NCg0KSW4gdGhpcyBjYXNlLCB0aGUgb2JqZWN0aXZlIGlzIHRvIHN0dWR5IGhvdyB0aGUgb3V0Y29tZSB2YXJpYWJsZSAoQ0xWKSBpcyByZWxhdGVkIHRvIHRoZSBpbmRlcGVuZGVudCB2YXJpYWJsZXMgYW5kIHRoZSBzdWJzZXF1ZW50IG1vZGVsIHRodXMgcHJvcG9zZWQgc2hvdWxkIGhlbHAgdGhlIGNvbXBhbnkgdG8gbWFrZSBhbiBpbmZvcm1lZCBkZWNpc2lvbiB3aXRoIHJlZ2FyZCB0byB0aGUga2luZCBvZiBjdXN0b21lciB0byB0YXJnZXQuDQoNCkl0IGlzIGEgcmVncmVzc2lvbiB0YXNrIHRvIHByZWRpY3QgaG93IG11Y2ggYSBnaXZlbiBjdXN0b21lciB3aWxsIGJlIHZhbHVhYmxlIHRvIGFuIGluc3VyYW5jZSBjb21wYW55LiANCg0KDQoNCg0KDQoNCiMjIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMNCg0KDQoNCg0KRm9sbG93aW5nIGFyZSB0aGUgcGFja2FnZXMgdXNlZC4NCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkgDQpsaWJyYXJ5KGNhcikgDQpsaWJyYXJ5KHpvbykNCmxpYnJhcnkobG10ZXN0KSANCmxpYnJhcnkoZHBseXIpIA0KbGlicmFyeShzdHJpbmdyKQ0KbGlicmFyeShjYXJldCkNCmxpYnJhcnkoZ2dwbG90MikgDQpsaWJyYXJ5KHRpbWVEYXRlKQ0KbGlicmFyeShwbG90bHkpDQpsaWJyYXJ5KHJlYWR4bCkNCmxpYnJhcnkoZ2dhbmltYXRlKQ0KbGlicmFyeShjb3JycGxvdCkNCmxpYnJhcnkoSG1pc2MpDQpsaWJyYXJ5KHZ0cmVlKQ0KbGlicmFyeShEYXRhRXhwbG9yZXIpDQpsaWJyYXJ5KGNhVG9vbHMpDQpsaWJyYXJ5KG5vcnRlc3QpDQpsaWJyYXJ5KG1vZGVscikNCmBgYA0KDQoNCg0KDQojIyMgUmVhZGluZyB0aGUgZGF0YXNldA0KYGBge3J9DQoNCk1hcmtldGluZ19DdXN0b21lcl9WYWx1ZV9BbmFseXNpc18yIDwtIHJlYWRfZXhjZWwoJ0M6L1VzZXJzL0hQL0Rvd25sb2Fkcy9NYXJrZXRpbmctQ3VzdG9tZXItVmFsdWUtQW5hbHlzaXMgMi54bHN4JykNCg0KDQpgYGANCg0KDQoNCiMjIyBTZXR0aW5nIHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eSBhbmQgb3ZlcnZpZXcgb2YgZGF0YXNldA0KDQpgYGB7cn0NCnNldC5zZWVkKDIyMykNCmhlYWQoTWFya2V0aW5nX0N1c3RvbWVyX1ZhbHVlX0FuYWx5c2lzXzIpDQoNCg0KYGBgDQoNCg0KDQojIyMgSGlzdG9ncmFtIG9mIHRoZSB0YXJnZXQgdmFyaWFibGUgKENMVikNCg0KVGhpcyBzaG93cyB1cyB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSB0YXJnZXQgdmFyaWFibGUsIHdoZXJlIHktYXhpcyBjb250YWlucyAgdGhlIHByb2JhYmlsaXR5IGRlbnNpdHkgb2YgdGhlIHRhcmdldCB2YXJpYWJsZS4NCg0KVGhpcyB0ZWxscyB1cyB0aGUgbW9uZXRhcnkgdmFsdWUgdGhhdCB0aGUgY3VzdG9tZXJzIHJlcHJlc2VudCB0byB0aGUgY29tcGFueS4NCg0KDQpgYGB7cn0NCkluc3VyYW5jZV9EYXRhc2V0IDwtIGRhdGEuZnJhbWUoTWFya2V0aW5nX0N1c3RvbWVyX1ZhbHVlX0FuYWx5c2lzXzIpDQpoaXN0KEluc3VyYW5jZV9EYXRhc2V0JEN1c3RvbWVyLkxpZmV0aW1lLlZhbHVlLA0KICAgICBicmVha3MgPSA4MDAsDQogICAgIGZyZXEgPSBGQUxTRSwNCiAgICAgbWFpbiA9ICJIaXN0b2dyYW0gb2YgQ0xWIiwgeGxhYiA9ICJDTFYiLCBib3JkZXIgPSAiQmx1ZSIpDQoNCmBgYA0KVGhpcyBwbG90IGluZGljYXRlcyB0aGF0IHRoZSBkaXN0cmlidXRpb24gaXMgaGVhdmlseSBwb3NpdGl2ZWx5IHNrZXdlZCwgbWVhbmluZyB0aGF0IGFuIG92ZXJ3aGVsbWluZyBtYWpvcml0eSBvZiB0aGUgY3VzdG9tZXJzIGhvbGQgbG93ZXIgY3VzdG9tZXIgbGlmZXRpbWUgdmFsdWUgdG8gdGhlIGNvbXBhbnkuQSB2ZXJ5IHNtYWxsIG51bWJlciBvZiBjdXN0b21lcnMgYXJlIGluIHRoZSBoaWdoZXIgYnJhY2tldCBvZiBsaWZldGltZSB2YWx1ZS4NCg0KVGhlICJpZGVhbCIgY3VzdG9tZXJzIHRvIHRoZSBjb21wYW55IGFyZSBzbWFsbCBpbiBudW1iZXIgYW5kIGlmIHRoZSBjb21wYW55IGlzIHRvIHR1cm4gYSBwcm9maXQgdGhleSBtdXN0IGFsc28gZm9jdXMgb24gY2F0ZXJpbmcgdG8gdGhlIGN1c3RvbWVycyB3aXRoIGxvd2VyIENMViBhcyB0aGV5IGFyZSBtb3JlIGluIG51bWJlci4NCg0KDQoNCg0KDQojIyMjIyMgRGVzY3JpcHRpb24gb2YgZGF0YXNldA0KDQpgYGB7cn0NCkluc3VyYW5jZV9EYXRhc2V0ICU+JSBpbnRyb2R1Y2UoKQ0KSW5zdXJhbmNlX0RhdGFzZXQgJT4lIHBsb3RfaW50cm8oKQ0KYGBgDQpUaGVyZSBhcmUgbm8gbnVsbCB2YWx1ZXMgaW4gdGhpcyBkYXRhc2V0Lg0KDQoNCg0KIyMjIENBVEVHT1JJQ0FMIFZBUklBQkxFUyBWSVNVQUxJWkFUSU9ODQoNCg0KDQojIyMjIFRvIHZpc3VhbGl6ZSB0aGUgZWZmZWN0IG9mIHN0YXRlIG9uIENMVg0KDQpgYGB7cn0NCg0KZ2dwbG90KEluc3VyYW5jZV9EYXRhc2V0LGFlcyAoeD1TdGF0ZSAsDQogICAgICAgICAgICAgIHk9Q3VzdG9tZXIuTGlmZXRpbWUuVmFsdWUpKSArIGdlb21fYmFyKHN0YXQ9InN1bW1hcnkiLGZ1bj0ic3VtIiwgd2lkdGg9MC41LCBmaWxsID0gIkJsdWUiKSsNCiAgbGFicyh4PSJTdGF0ZSIseSA9ICJDdXN0b21lciBMaWZlIFRpbWUgVmFsdWUiLCBmaWxsPSJTdGF0ZSIpICsNCiAgZ2d0aXRsZSgiU3VtIG9mIENMViBjb250cmlidXRpb24gYnkgU3RhdGUgIikNCg0KYGBgDQpJbiB0aGlzIGNhc2UsIHdlJ3JlIGxvb2tpbmcgYXQgaG93IG11Y2ggZWZmZWN0IGEgY3VzdG9tZXIncyBzdGF0ZSBoYXMgb24gQ0xWLiBJbiBvdGhlciB3b3Jkcywgd2UgYXJlIHRyeWluZyB0byBmaW5kIGlmIGEgY3VzdG9tZXIgZnJvbSBhIHBhcnRpY3VsYXIgc3RhdGUgaXMgbW9yZSB2YWx1YWJsZSB0byB0aGUgY29tcGFueSB0aGFuIG90aGVyIHN0YXRlcy4gDQoNCkZyb20gdGhlIGFib3ZlIGNoYXJ0IGl0IHdvdWxkIGFwcGVhciB0aGF0IHRoZSBjb21wYW55IHNob3VsZCBmb2N1cyB0aGVpciBlZmZvcnRzIG9uIHN0YXRlcyBsaWtlIENhbGlmb3JuaWEgb3IgT3JlZ29uLCBzaW5jZSB0aGUgc3VtIG9mIENMViBmcm9tIHRoZXNlIHN0YXRlcyBhcmUgaGlnaGVyLiBBcyB3ZSBjYW4gc2VlIGluIHRoZSBjaGFydCBiZWxvdyB0aGUgcG9wdWxhdGlvbiBvZiB0aGVzZSBzdGF0ZXMgaXMgYSBmYWN0b3IgZm9yIHRoZSBoaWdoIENMViBvYnRhaW5lZC4NCmBgYHtyfQ0KY291bnRfc3RhdGUgPC0gdGFibGUoSW5zdXJhbmNlX0RhdGFzZXQkU3RhdGUpDQpiYXJwbG90KGNvdW50X3N0YXRlLCANCiAgICAgICAgbWFpbiA9ICJDb3VudCBwbG90IG9mIFN0YXRlIixjb2wgPSAiQmx1ZSIsDQogICAgICAgIHhsYWIgPSAiU3RhdGUiLCB5bGFiID0gIkNvdW50IikNCg0KYGBgDQoNCg0KDQpMZXQgdXMgZXhwbG9yZSB0aGlzIGJ5IGNvbnNpZGVyaW5nIHRoZSBtZWFuIG9mIHRoZSBDTFYgYnkgc3RhdGUgaW4gdGhlIHN1YnNlcXVlbnQgY2hhcnQuIFRoaXMgbWVhc3VyZSB3aWxsIGFjY291bnQgZm9yIHRoZSBsYXJnZXIgcG9wdWxhdGlvbnMgb2Ygc3RhdGVzIGxpa2UgQ2FsaWZvcm5pYSBhbmQgT3JlZ29uLg0KDQpgYGB7cn0NCmFnZ0RhdGEgPC0gYWdncmVnYXRlKHggPSBJbnN1cmFuY2VfRGF0YXNldCRDdXN0b21lci5MaWZldGltZS5WYWx1ZSwNCiAgICAgICAgICAgICAgICAgICAgIGJ5PWxpc3QoU3RhdGUgPSBJbnN1cmFuY2VfRGF0YXNldCRTdGF0ZSksDQogICAgICAgICAgICAgICAgICAgICBGVU4gPSBtZWFuKQ0KZ2dwbG90KGRhdGEgPSBhZ2dEYXRhLCBhZXMoeCA9IFN0YXRlLCB5ID0gcHJvcC50YWJsZShzdGF0KGFnZ0RhdGEkeCkpLCBmaWxsID0gU3RhdGUsIGxhYmVsID0gc2NhbGVzOjpwZXJjZW50KHByb3AudGFibGUoc3RhdChhZ2dEYXRhJHgpKSkpKSArDQogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArIA0KICBnZW9tX3RleHQoc3RhdCA9ICdpZGVudGl0eScsIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoLjkpLCAgdmp1c3QgPSAtMC41LCBzaXplID0gMykgKyANCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKyANCiAgbGFicyh4ID0gJ1N0YXRlJywgeSA9ICdDTFYgaW4gUGVyY2VudGFnZScsIGZpbGwgPSAnU3RhdGUnKSArIA0KICBnZ3RpdGxlKCJNZWFuIGNvbnRyaWJ1dGlvbiB0byBDTFYgYnkgU3RhdGUiKQ0KDQpgYGANCldoZW4gdGhlIG1lYW4gb2YgdGhlIENMViBpcyBjb21wdXRlZCB3ZSBjYW4gc2VlIHRoYXQgbm8gcGFydGljdWxhciBzdGF0ZSBpcyBhbnkgbW9yZSBlY29ub21pY2FsbHkgdmFsdWFibGUgdGhhbiB0aGUgb3RoZXIsIGFzIHRoZSBjdXN0b21lcnMgZnJvbSBlYWNoIHN0YXRlIG9uIGFuIGF2ZXJhZ2UgY29udHJpYnV0ZSBlcXVhbGx5IHRvIHRoZSB0YXJnZXQgdmFyaWFibGUoQ0xWKS4NClRoaXMgdGVsbHMgdXMgdGhhdCBzdGF0ZSBpcyBhIHdlYWsgaW5kaWNhdG9yIHZhcmlhYmxlIGZvciB0aGUgQ0xWLg0KDQoNCg0KIyMjIyBUbyB2aXN1YWxpemUgdGhlIGVmZmVjdCBvZiBFZHVjYXRpb24gb24gQ0xWDQoNCg0KYGBge3J9DQoNCmdncGxvdChJbnN1cmFuY2VfRGF0YXNldCxhZXMgKHg9RWR1Y2F0aW9uICwNCiAgICAgICAgICAgICAgeT1DdXN0b21lci5MaWZldGltZS5WYWx1ZSkpICsgZ2VvbV9iYXIoc3RhdD0ic3VtbWFyeSIsZnVuPSJzdW0iLCB3aWR0aD0wLjUsIGZpbGwgPSAiQmx1ZSIpKw0KICBsYWJzKHg9IkVkdWNhdGlvbiIseSA9ICJDdXN0b21lciBMaWZlIFRpbWUgVmFsdWUiLCBmaWxsPSJFZHVjYXRpb24iKSArDQogIGdndGl0bGUoIkNvbnRyaWJ1dGlvbiBvZiBDTFYgYnkgRWR1Y2F0aW9uIikNCg0KYGBgDQoNCmBgYHtyfQ0KY291bnRfZWR1Y2F0aW9uIDwtIHRhYmxlKEluc3VyYW5jZV9EYXRhc2V0JEVkdWNhdGlvbikNCmJhcnBsb3QoY291bnRfZWR1Y2F0aW9uLCANCiAgICAgICAgbWFpbiA9ICJDb3VudCBwbG90IG9mIEVkdWNhdGlvbiIsY29sID0gIkJsdWUiLA0KICAgICAgICB4bGFiID0gIkVkdWNhdGlvbiIsIHlsYWIgPSAiQ291bnQiKQ0KDQpgYGANCg0KDQoNCmBgYHtyfQ0KYWdnRGF0YSA8LSBhZ2dyZWdhdGUoeCA9IEluc3VyYW5jZV9EYXRhc2V0JEN1c3RvbWVyLkxpZmV0aW1lLlZhbHVlLA0KICAgICAgICAgICAgICAgICAgICAgYnk9bGlzdChFZHVjYXRpb24gPSBJbnN1cmFuY2VfRGF0YXNldCRFZHVjYXRpb24pLA0KICAgICAgICAgICAgICAgICAgICAgRlVOID0gbWVhbikNCmdncGxvdChkYXRhID0gYWdnRGF0YSwgYWVzKHggPSBFZHVjYXRpb24sIHkgPSBwcm9wLnRhYmxlKHN0YXQoYWdnRGF0YSR4KSksIGZpbGwgPSBFZHVjYXRpb24sIGxhYmVsID0gc2NhbGVzOjpwZXJjZW50KHByb3AudGFibGUoc3RhdChhZ2dEYXRhJHgpKSkpKSArDQogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArIA0KICBnZW9tX3RleHQoc3RhdCA9ICdpZGVudGl0eScsIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoLjkpLCAgdmp1c3QgPSAtMC41LCBzaXplID0gMykgKyANCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKyANCiAgbGFicyh4ID0gJ0VkdWNhdGlvbicsIHkgPSAnQ0xWIGluIFBlcmNlbnRhZ2UnLCBmaWxsID0gJ0VkdWNhdGlvbicpICsgDQogIGdndGl0bGUoIkNvbnRyaWJ1dGlvbiB0byBDTFYgYnkgRWR1Y2F0aW9uIikNCg0KYGBgDQoNCg0KSW4gdGhlIGZpcnN0IHBsb3QgaXQgYXBwZWFycyBhcyB0aG91Z2ggdGhlIGNvbnRyaWJ1dGlvbiBvZiBjdXN0b21lcnMgaGF2aW5nIGRvY3RvcnMgYW5kIE1hc3RlcidzIHF1YWxpZmljYXRpb24gaXMgbXVjaCBsZXNzZXIgY29tcGFyZWQgdG8gdGhlIGN1c3RvbWVycyB3aXRoIG90aGVyIHF1YWxpZmljYXRpb25zLCB3aGljaCBpcyBjb3VudGVyLWludHVpdGl2ZS4gDQpCdXQgaWYgd2UgZmFjdG9yIGluIHRoZSBjb3VudCBvZiBjdXN0b21lcnMgZnJvbSBkaWZmZXJlbnQgbGV2ZWxzIG9mIGVkdWNhdGlvbiwgd2Ugbm90aWNlIHRoYXQgc2luY2UgdGhlcmUgYXJlIG1vcmUgY3VzdG9tZXJzIGhhdmluZyBCYWNoZWxvcidzLCBDb2xsZWdlIGFuZCBIaWdoIGxldmVsIHF1YWxpZmljYXRpb24sIHRoZSBjb250cmlidXRpb24gZnJvbSB0aGVzZSBjYXRlZ29yaWVzIGlzIG1vcmUsIGFzIHNob3duIGluIHRoZSBzZWNvbmQgY2hhcnQuDQpUaGlzIHBvaW50IGlzIGZ1cnRoZXIgc3VwcG9ydGVkIGJ5IHRoZSBuZXh0IGNoYXJ0IHdoZXJlIHRoZSBhdmVyYWdlIGNvbnRyaWJ1dGlvbiBvZiBlYWNoIGNsYXNzIG9mIHF1YWxpZmljYXRpb24gaXMgYWxtb3N0IHRoZSBzYW1lLCB3aGljaCBpbmRpY2F0ZXMgdGhhdCB0aGUgdmFsdWUgb2YgaW5zdXJhbmNlIHBvbGljaWVzIHB1cmNoYXNlZCBieSB0aGUgY3VzdG9tZXJzIGhhdmluZyBkb2N0b3JzIGFuZCBNYXN0ZXIncyBxdWFsaWZpY2F0aW9uIGlzIG11Y2ggaGlnaGVyIHRoYW4gdGhlIGN1c3RvbWVycyBoYXZpbmcgb3RoZXIgcXVhbGlmaWNhdGlvbnMuIA0KDQoNCg0KDQoNCg0KDQoNCiMjIyMgVG8gdmlzdWFsaXplIHRoZSBlZmZlY3Qgb2YgQ292ZXJhZ2Ugb24gQ0xWDQoNCmBgYHtyfQ0KZ2dwbG90KEluc3VyYW5jZV9EYXRhc2V0LGFlcyAoeD1Db3ZlcmFnZSAsDQogICAgICAgICAgICAgIHk9Q3VzdG9tZXIuTGlmZXRpbWUuVmFsdWUpKSArIGdlb21fYmFyKHN0YXQ9InN1bW1hcnkiLGZ1bj0ic3VtIiwgd2lkdGg9MC41LCBmaWxsID0gIkJsdWUiKSsNCiAgbGFicyh4PSJDb3ZlcmFnZSIseSA9ICJDdXN0b21lciBMaWZlIFRpbWUgVmFsdWUiLCBmaWxsPSJDb3ZlcmFnZSIpICsNCiAgZ2d0aXRsZSgiQ29udHJpYnV0aW9uIHRvIENMViBieSBDb3ZlcmFnZSIpDQpgYGANCmBgYHtyfQ0KY291bnRfY292ZXJhZ2UgPC0gdGFibGUoSW5zdXJhbmNlX0RhdGFzZXQkQ292ZXJhZ2UpDQpiYXJwbG90KGNvdW50X2NvdmVyYWdlLCANCiAgICAgICAgbWFpbiA9ICJDb3VudCBwbG90IG9mIENvdmVyYWdlIixjb2wgPSAiQmx1ZSIsDQogICAgICAgIHhsYWIgPSAiQ292ZXJhZ2UiLCB5bGFiID0gIkNvdW50IikNCg0KYGBgDQoNCg0KDQpgYGB7cn0NCmFnZ0RhdGEgPC0gYWdncmVnYXRlKHggPSBJbnN1cmFuY2VfRGF0YXNldCRDdXN0b21lci5MaWZldGltZS5WYWx1ZSwNCiAgICAgICAgICAgICAgICAgICAgIGJ5PWxpc3QoQ292ZXJhZ2UgPSBJbnN1cmFuY2VfRGF0YXNldCRDb3ZlcmFnZSksDQogICAgICAgICAgICAgICAgICAgICBGVU4gPSBtZWFuKQ0KZ2dwbG90KGRhdGEgPSBhZ2dEYXRhLCBhZXMoeCA9IENvdmVyYWdlLCB5ID0gcHJvcC50YWJsZShzdGF0KGFnZ0RhdGEkeCkpLCBmaWxsID0gQ292ZXJhZ2UsIGxhYmVsID0gc2NhbGVzOjpwZXJjZW50KHByb3AudGFibGUoc3RhdChhZ2dEYXRhJHgpKSkpKSArDQogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArIA0KICBnZW9tX3RleHQoc3RhdCA9ICdpZGVudGl0eScsIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoLjkpLCAgdmp1c3QgPSAtMC41LCBzaXplID0gMykgKyANCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKyANCiAgbGFicyh4ID0gJ0NvdmVyYWdlJywgeSA9ICdDTFYgaW4gUGVyY2VudGFnZScsIGZpbGwgPSAnQ292ZXJhZ2UnKSArIA0KICBnZ3RpdGxlKCIgTWVhbiBDTFYgQ29udHJpYnV0aW9uIGJ5IENvdmVyYWdlIikNCmBgYA0KDQpJdCB3b3VsZCBiZSBhcHBhcmVudCBmcm9tIHRoZSBmaXJzdCBjaGFydCB0aGF0IHRoZSBCYXNpYyBjb3ZlcmFnZSBwbGFuIGhhcyB0aGUgbW9zdCBjb250cmlidXRpb24gdG8gQ0xWIGJlY2F1c2UgdGhlcmUgYXJlIG1vcmUgdGFrZXJzIGZvciB0aGUgYmFzaWMgY292ZXJhZ2UgcGxhbiwgYXMgcHJvdmVuIGJ5IHRoZSBzZWNvbmQgY2hhcnQuDQpJbiB0aGUgdGhpcmQgY2hhcnQsIGhvd2V2ZXIsIHdlIGNhbiBzZWUgdGhhdCBldmVuIHRob3VnaCBwcmVtaXVtIGNvdmVyYWdlIHBsYW5zIGFjY291bnRlZCBmb3IgdGhlIGxlYXN0IHZvbHVtZSBvZiBDTFYsIG9uIGFuIGF2ZXJhZ2UgYSBjdXN0b21lciBoYXZpbmcgdGhlIHByZW1pdW0gY292ZXJhZ2UgaGFzIGEgZ3JlYXRlciBjb250cmlidXRpb24gdG8gQ0xWLg0KDQoNCg0KDQoNCg0KDQoNCg0KDQojIyMjIFRvIHZpc3VhbGl6ZSB0aGUgZWZmZWN0IG9mIEVtcGxveW1lbnQgU3RhdHVzIG9uIENMVg0KDQoNCg0KYGBge3J9DQpnZ3Bsb3QoSW5zdXJhbmNlX0RhdGFzZXQsYWVzICh4PUVtcGxveW1lbnRTdGF0dXMgLA0KICAgICAgICAgICAgICB5PUN1c3RvbWVyLkxpZmV0aW1lLlZhbHVlKSkgKyBnZW9tX2JhcihzdGF0PSJzdW1tYXJ5IixmdW49InN1bSIsIHdpZHRoPTAuNSwgZmlsbCA9ICJCbHVlIikrDQogIGxhYnMoeD0iRW1wbG95bWVudCBTdGF0dXMiLHkgPSAiQ3VzdG9tZXIgTGlmZSBUaW1lIFZhbHVlIiwgZmlsbD0iRW1wbG95bWVudCBTdGF0dXMiKSArDQogIGdndGl0bGUoIkNvbnRyaWJ1dGlvbiB0byBDTFYgYnkgRW1wbG95bWVudCBTdGF0dXMiKQ0KYGBgDQpgYGB7cn0NCmNvdW50X2VtcGxveW1lbnRzdGF0dXMgPC0gdGFibGUoSW5zdXJhbmNlX0RhdGFzZXQkRW1wbG95bWVudFN0YXR1cykNCmJhcnBsb3QoY291bnRfZW1wbG95bWVudHN0YXR1cywgDQogICAgICAgIG1haW4gPSAiQ291bnQgcGxvdCBvZiBFbXBsb3ltZW50IFN0YXR1cyIsY29sID0gIkJsdWUiLA0KICAgICAgICB4bGFiID0gIkVtcGxveW1lbnQgU3RhdHVzIiwgeWxhYiA9ICJDb3VudCIpDQoNCmBgYA0KDQpgYGB7cn0NCmFnZ0RhdGEgPC0gYWdncmVnYXRlKHggPSBJbnN1cmFuY2VfRGF0YXNldCRDdXN0b21lci5MaWZldGltZS5WYWx1ZSwNCiAgICAgICAgICAgICAgICAgICAgIGJ5PWxpc3QoRW1wbG95bWVudFN0YXR1cyA9IEluc3VyYW5jZV9EYXRhc2V0JEVtcGxveW1lbnRTdGF0dXMpLA0KICAgICAgICAgICAgICAgICAgICAgRlVOID0gbWVhbikNCmdncGxvdChkYXRhID0gYWdnRGF0YSwgYWVzKHggPSBFbXBsb3ltZW50U3RhdHVzLCB5ID0gcHJvcC50YWJsZShzdGF0KGFnZ0RhdGEkeCkpLCBmaWxsID0gRW1wbG95bWVudFN0YXR1cywgbGFiZWwgPSBzY2FsZXM6OnBlcmNlbnQocHJvcC50YWJsZShzdGF0KGFnZ0RhdGEkeCkpKSkpICsNCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsgDQogIGdlb21fdGV4dChzdGF0ID0gJ2lkZW50aXR5JywgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSguOSksICB2anVzdCA9IC0wLjUsIHNpemUgPSAzKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArIA0KICBsYWJzKHggPSAnRW1wbG95bWVudCBTdGF0dXMnLCB5ID0gJ0NMViBpbiBQZXJjZW50YWdlJywgZmlsbCA9ICdFbXBsb3ltZW50IFN0YXR1cycpICsgDQogIGdndGl0bGUoIkNvbnRyaWJ1dGlvbiB0byBDTFYgYnkgRW1wbG95bWVudCBTdGF0dXMiKQ0KYGBgDQpJbiB0aGUgZmlyc3QgY2hhcnQsIGl0IGlzIGV2aWRlbnQgdGhhdCB0aGUgY3VzdG9tZXJzIHdobyBhcmUgZW1wbG95ZWQgYXJlIG9mIGdyZWF0ZXIgdmFsdWUgdG8gdGhlIGNvbXBhbnkgdGhhbiB0aGUgb3RoZXIgY2F0ZWdvcmllcy4gVGhlIGluZmVyZW5jZSBkcmF3biBmcm9tIHRoaXMgaXMgc3RyYWlnaHRmb3J3YXJkLCBpLmUgZW1wbG95ZWQgY3VzdG9tZXJzIGFyZSBtb3JlIGxpa2VseSB0byBiZSBhYmxlIHRvIGFmZm9yZCB0aGUgcHJlbWl1bXMgYW5kIHRoZXJlZm9yZSBjb250cmlidXRlIGEgbWFqb3IgY2h1bmsgdG8gdGhlIENMVi4NCg0KDQpCdXQgaW4gdGhlIHNlY29uZCBjaGFydCwgd2hlbiB3ZSBhY2NvdW50IGZvciB0aGUgY29udHJpYnV0aW9uIG9uIGFuIGF2ZXJhZ2UgYnkgdGhlIGVtcGxveW1lbnQgc3RhdHVzLCB3ZSBub3RpY2UgdGhhdCBhbGwgYXJlIGVxdWFsbHkgY29udHJpYnV0aW5nIHRvIENMVi4NClRoZSByZWFzb24gd2h5IHRoZSB0aGUgZW1wbG95ZWQgc3RhdHVzIGhhcyBzdWNoIGEgaGlnaCBjb250cmlidXRpb24gdG8gdGhlIENMViBpcyBiZWNhdXNlIHRoZSBudW1iZXIgb2YgY3VzdG9tZXJzIHdobyBhcmUgZW1wbG95ZWQgaXMgaGlnaC4NCg0KDQoNCg0KDQojIyMjIFRvIHZpc3VhbGl6ZSB0aGUgZWZmZWN0IG9mIExvY2F0aW9uIENvZGUgb24gQ0xWLg0KDQpgYGB7cn0NCmdncGxvdChJbnN1cmFuY2VfRGF0YXNldCxhZXMgKHg9TG9jYXRpb24uQ29kZSAsDQogICAgICAgICAgICAgIHk9Q3VzdG9tZXIuTGlmZXRpbWUuVmFsdWUpKSArIGdlb21fYmFyKHN0YXQ9InN1bW1hcnkiLGZ1bj0ic3VtIiwgd2lkdGg9MC41LCBmaWxsID0gIkJsdWUiKSsNCiAgbGFicyh4PSJMb2NhdGlvbiBDb2RlIix5ID0gIkN1c3RvbWVyIExpZmUgVGltZSBWYWx1ZSIsIGZpbGw9IkxvY2F0aW9uIENvZGUiKSArDQogIGdndGl0bGUoIkNvbnRyaWJ1dGlvbiB0byBDTFYgYnkgTG9jYXRpb24gQ29kZSIpDQpgYGANCmBgYHtyfQ0KY291bnRfbG9jYXRpb25jb2RlIDwtIHRhYmxlKEluc3VyYW5jZV9EYXRhc2V0JExvY2F0aW9uLkNvZGUpDQpiYXJwbG90KGNvdW50X2xvY2F0aW9uY29kZSwgDQogICAgICAgIG1haW4gPSAiQ291bnQgcGxvdCBvZiBMb2NhdGlvbiBDb2RlIixjb2wgPSAiQmx1ZSIsDQogICAgICAgIHhsYWIgPSAiTG9jYXRpb24gQ29kZSIsIHlsYWIgPSAiQ291bnQiKQ0KYGBgDQoNCmBgYHtyfQ0KYWdnRGF0YSA8LSBhZ2dyZWdhdGUoeCA9IEluc3VyYW5jZV9EYXRhc2V0JEN1c3RvbWVyLkxpZmV0aW1lLlZhbHVlLA0KICAgICAgICAgICAgICAgICAgICAgYnk9bGlzdChMb2NhdGlvbi5Db2RlID0gSW5zdXJhbmNlX0RhdGFzZXQkTG9jYXRpb24uQ29kZSksDQogICAgICAgICAgICAgICAgICAgICBGVU4gPSBtZWFuKQ0KZ2dwbG90KGRhdGEgPSBhZ2dEYXRhLCBhZXMoeCA9IExvY2F0aW9uLkNvZGUsIHkgPSBwcm9wLnRhYmxlKHN0YXQoYWdnRGF0YSR4KSksIGZpbGwgPSBMb2NhdGlvbi5Db2RlLCBsYWJlbCA9IHNjYWxlczo6cGVyY2VudChwcm9wLnRhYmxlKHN0YXQoYWdnRGF0YSR4KSkpKSkgKw0KICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKyANCiAgZ2VvbV90ZXh0KHN0YXQgPSAnaWRlbnRpdHknLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKC45KSwgIHZqdXN0ID0gLTAuNSwgc2l6ZSA9IDMpICsgDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQpICsgDQogIGxhYnMoeCA9ICdMb2NhdGlvbiBDb2RlJywgeSA9ICdDTFYgaW4gUGVyY2VudGFnZScsIGZpbGwgPSAnTG9jYXRpb24gQ29kZScpICsgDQogIGdndGl0bGUoIkNvbnRyaWJ1dGlvbiB0byBDTFYgYnkgTG9jYXRpb24gQ29kZSIpDQpgYGANCg0KSW4gdGhlIGZpcnN0IGNoYXJ0IGl0IGFwcGVhcnMgYXMgdGhvdWdoIGN1c3RvbWVycyBmcm9tIHRoZSBzdWJ1cmJhbiBsb2NhdGlvbiBhcmUgYSBiZXR0ZXIgY29udHJpYnV0b3IgdG8gQ0xWIHRoYW4gdGhlIG90aGVyIGFyZWFzLiANCkZyb20gdGhlIHNlY29uZCBjaGFydCBpdCBpcyBjbGVhciB0aGF0IGl0IGlzIGJlY2F1c2Ugb2YgdGhlIGhpZ2hlciBudW1iZXIgb2Ygc3Vic2NyaWJlcnMgZnJvbSBzdWJ1cmJhbiBhcmVhcy4NCg0KQnV0IGZyb20gdGhlIHRoaXJkLCB3ZSBzZWUgdGhhdCBhbGwgb2YgdGhlIGxvY2F0aW9uIGNvZGVzIG9uIGFuIGF2ZXJhZ2UgY29udHJpYnV0ZSBlcXVhbGx5IHRvIHRoZSBDTFYgYW5kIHRoZXJlZm9yZSBMb2NhdGlvbiBDb2RlIGlzIGEgd2VhayBwcmVkaWN0b3Igb2YgdGhlIENMViBvbiBpdHMgb3duLg0KDQoNCiMjIyMgRWZmZWN0IG9uIENMViBieSBTdGF0ZSBhbmQgTG9jYXRpb24gQ29kZQ0KYGBge3J9DQpwMTwtcGxvdF9seShJbnN1cmFuY2VfRGF0YXNldCwgeCA9flN0YXRlLCB5ID1+SW5zdXJhbmNlX0RhdGFzZXQkYEN1c3RvbWVyLkxpZmV0aW1lLlZhbHVlYCx0eXBlPSdiYXInLGNvbG9yPX5JbnN1cmFuY2VfRGF0YXNldCRgTG9jYXRpb24uQ29kZWApIA0KbGF5b3V0KHAxLCB0aXRsZSA9J0NMViB3LnIudCBTdGF0ZSBhbmQgTG9jYXRpb24gQ29kZScsIHlheGlzID0gbGlzdCh0aXRsZSA9ICdDTFYgJykpDQoNCmBgYA0KDQpDYWxpZm9ybmlhIGFuZCBPcmVnb24gb3V0cGVyZm9ybSB0aGUgb3RoZXIgc3RhdGVzIGluIGV2ZXJ5IGxvY2F0aW9uIGNvZGUgd2l0aCByZWdhcmQgdG8gQ0xWLg0KDQoNCiMjIyMgVG8gdmlzdWFsaXplIHRoZSBlZmZlY3Qgb2YgTWFyaXRhbCBTdGF0dXMgb24gQ0xWDQpgYGB7ciB3YXJuaW5nPUZBTFNFfQ0KZ2dwbG90KEluc3VyYW5jZV9EYXRhc2V0LGFlcyAoeD1JbnN1cmFuY2VfRGF0YXNldCQiTWFyaXRhbC5TdGF0dXMiLCB5PUluc3VyYW5jZV9EYXRhc2V0JCJDdXN0b21lci5MaWZldGltZS5WYWx1ZSIpKSArIGdlb21fYmFyKHN0YXQ9InN1bW1hcnkiLGZ1bj0ic3VtIiwgd2lkdGg9MC41LCBmaWxsID0gIkJsdWUiKSArDQogIGxhYnMoeD0iTWFyaXRhbCBTdGF0dXMiLHkgPSAiQ3VzdG9tZXIgTGlmZSBUaW1lIFZhbHVlIiwgZmlsbD0iTWFyaXRhbCBTdGF0dXMiKSArIA0KICBnZ3RpdGxlKCJWaXN1YWxpemF0aW9uIG9mIENMViB3cnQgTWFyaXRhbCBTdGF0dXMiKQ0KICANCmBgYA0KYGBge3J9DQpjb3VudF9tYXJpdGFsc3RhdHVzIDwtIHRhYmxlKEluc3VyYW5jZV9EYXRhc2V0JE1hcml0YWwuU3RhdHVzKQ0KYmFycGxvdChjb3VudF9tYXJpdGFsc3RhdHVzLCANCiAgICAgICAgbWFpbiA9ICJDb3VudCBwbG90IG9mIE1hcml0YWwgU3RhdHVzIixjb2wgPSAiQmx1ZSIsDQogICAgICAgIHhsYWIgPSAiTWFyaXRhbCBTdGF0dXMiLCB5bGFiID0gIkNvdW50IikNCmBgYA0KDQoNCmBgYHtyfQ0KYWdnRGF0YSA8LSBhZ2dyZWdhdGUoeCA9IEluc3VyYW5jZV9EYXRhc2V0JEN1c3RvbWVyLkxpZmV0aW1lLlZhbHVlLA0KICAgICAgICAgICAgICAgICAgICAgYnk9bGlzdChNYXJpdGFsLlN0YXR1cyA9IEluc3VyYW5jZV9EYXRhc2V0JE1hcml0YWwuU3RhdHVzKSwNCiAgICAgICAgICAgICAgICAgICAgIEZVTiA9IG1lYW4pDQpnZ3Bsb3QoZGF0YSA9IGFnZ0RhdGEsIGFlcyh4ID0gTWFyaXRhbC5TdGF0dXMsIHkgPSBwcm9wLnRhYmxlKHN0YXQoYWdnRGF0YSR4KSksIGZpbGwgPSBNYXJpdGFsLlN0YXR1cywgbGFiZWwgPSBzY2FsZXM6OnBlcmNlbnQocHJvcC50YWJsZShzdGF0KGFnZ0RhdGEkeCkpKSkpICsNCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsgDQogIGdlb21fdGV4dChzdGF0ID0gJ2lkZW50aXR5JywgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSguOSksICB2anVzdCA9IC0wLjUsIHNpemUgPSAzKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArIA0KICBsYWJzKHggPSAnTWFyaXRhbFN0YXR1cycsIHkgPSAnQ0xWIGluIFBlcmNlbnRhZ2UnLCBmaWxsID0gJ01hcml0YWwgU3RhdHVzJykgKyANCiAgZ2d0aXRsZSgiQ29udHJpYnV0aW9uIHRvIENMViBieSBNYXJpdGFsIFN0YXR1cyIpDQpgYGANCldlIG1pZ2h0IGVycm9uZW91c2x5IGNvbmNsdWRlIGZyb20gdGhlIGZpcnN0IGFuZCB0aGUgc2Vjb25kIGNoYXJ0IHRoYXQgbW9zdCBtYXJyaWVkIGN1c3RvbWVycyBoYXZlIGhpZ2ggQ0xWIGJ1dCB0aGUgdGhpcmQgY2hhcnQgc2hvd3MgdXMgdGhhdCBvbiBhbiBhdmVyYWdlIHRoZXJlIGlzIG5vIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgY29udHJpYnV0aW9ucyBvZiBlYWNoIHN1Yi1jYXRlZ29yeSB0byB0aGUgQ0xWLg0KDQoNCiMjIyMgVG8gdmlzdWFsaXplIHRoZSBlZmZlY3Qgb2YgUG9saWN5IFR5cGUgb24gQ0xWDQpgYGB7cn0NCmdncGxvdChJbnN1cmFuY2VfRGF0YXNldCxhZXMgKHg9UG9saWN5LlR5cGUgLA0KICAgICAgICAgICAgICB5PUN1c3RvbWVyLkxpZmV0aW1lLlZhbHVlKSkgKyBnZW9tX2JhcihzdGF0PSJzdW1tYXJ5IixmdW49InN1bSIsIHdpZHRoPTAuNSwgZmlsbCA9ICJCbHVlIikrDQogIGxhYnMoeD0iUG9saWN5IFR5cGUiLHkgPSAiQ3VzdG9tZXIgTGlmZSBUaW1lIFZhbHVlIiwgZmlsbD0iUG9saWN5IFR5cGUiKSArDQogIGdndGl0bGUoIkNvbnRyaWJ1dGlvbiB0byBDTFYgYnkgUG9saWN5IFR5cGUiKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KEluc3VyYW5jZV9EYXRhc2V0LGFlcyAoeD1Qb2xpY3kuVHlwZSkpICsgDQogICAgICAgICBnZW9tX2JhcihzdGF0PSJjb3VudCIsIHdpZHRoPTAuNSwgZmlsbCA9ICJCbHVlIikgKw0KICBsYWJzKHg9IlBvbGljeSBUeXBlIix5ID0gIkNvdW50IiwgZmlsbD0iUG9saWN5IFR5cGUiKSArDQogIGdndGl0bGUoIkNvdW50IG9mIFBvbGljeSBUeXBlIikNCmBgYA0KDQpgYGB7cn0NCmFnZ0RhdGEgPC0gYWdncmVnYXRlKHggPSBJbnN1cmFuY2VfRGF0YXNldCRDdXN0b21lci5MaWZldGltZS5WYWx1ZSwNCiAgICAgICAgICAgICAgICAgICAgIGJ5PWxpc3QoUG9saWN5LlR5cGUgPSBJbnN1cmFuY2VfRGF0YXNldCRQb2xpY3kuVHlwZSksDQogICAgICAgICAgICAgICAgICAgICBGVU4gPSBtZWFuKQ0KZ2dwbG90KGRhdGEgPSBhZ2dEYXRhLCBhZXMoeCA9IFBvbGljeS5UeXBlLCB5ID0gcHJvcC50YWJsZShzdGF0KGFnZ0RhdGEkeCkpLCBmaWxsID0gUG9saWN5LlR5cGUsIGxhYmVsID0gc2NhbGVzOjpwZXJjZW50KHByb3AudGFibGUoc3RhdChhZ2dEYXRhJHgpKSkpKSArDQogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArIA0KICBnZW9tX3RleHQoc3RhdCA9ICdpZGVudGl0eScsIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoLjkpLCAgdmp1c3QgPSAtMC41LCBzaXplID0gMykgKyANCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKyANCiAgbGFicyh4ID0gJ1BvbGljeSBUeXBlJywgeSA9ICdDTFYgaW4gUGVyY2VudGFnZScsIGZpbGwgPSAnUG9saWN5IFR5cGUnKSArIA0KICBnZ3RpdGxlKCJNZWFuIENMViBjb250cmlidXRpb24gIGJ5IFBvbGljeSBUeXBlIikNCmBgYA0KU2ltaWxhciByZXN1bHRzIGFyZSBvYnRhaW5lZCBhcyBiZWZvcmUuIEluaXRpYWxseSBpdCBtYXkgYXBwZWFyIHRoYXQgdGhlIHBlcnNvbmFsIGF1dG8gcG9saWN5IG1pZ2h0IGJlIGEgbWFqb3JpdHkgY29udHJpYnV0b3IgYnV0IGZ1cnRoZXIgYW5hbHlzaXMgc2hvd3MgdGhhdCBpdCBzZWVtcyBtb3JlIGxpa2VseSB0aGF0IGN1c3RvbWVycyB3aG8gaGF2ZSBwdXJjaGFzZWQgdGhlIFNwZWNpYWwgQXV0byBoYXZlIGEgZ3JlYXRlciBDTFYuDQoNCg0KDQojIyMjIFRvIHZpc3VhbGl6ZSB0aGUgZWZmZWN0IG9mIGdlbmRlciBvbiBDTFYNCg0KDQoNCmBgYHtyfQ0KZ2dwbG90KEluc3VyYW5jZV9EYXRhc2V0LGFlcyAoeD1HZW5kZXIpKSArIA0KICAgICAgICAgZ2VvbV9iYXIoc3RhdD0iY291bnQiLCB3aWR0aD0wLjUsIGZpbGwgPSAiQmx1ZSIpICsNCiAgbGFicyh4PSJHZW5kZXIiLHkgPSAiQ291bnQiKSArDQogIGdndGl0bGUoIkNvdW50IG9mIEdlbmRlciIpDQpgYGANCg0KDQoNCmBgYHtyfQ0KZ2dwbG90KEluc3VyYW5jZV9EYXRhc2V0LGFlcyAoeD1HZW5kZXIgLA0KICAgICAgICAgICAgICB5PUN1c3RvbWVyLkxpZmV0aW1lLlZhbHVlKSkgKyBnZW9tX2JhcihzdGF0PSJzdW1tYXJ5IixmdW49InN1bSIsIHdpZHRoPTAuNSwgZmlsbCA9ICJCbHVlIikrDQogIGxhYnMoeD0iR2VuZGVyIix5ID0gIkN1c3RvbWVyIExpZmUgVGltZSBWYWx1ZSIsIGZpbGw9IkdlbmRlciIpICsNCiAgZ2d0aXRsZSgiQ29udHJpYnV0aW9uIHRvIENMViBieSBHZW5kZXIiKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KEluc3VyYW5jZV9EYXRhc2V0LGFlcyAoeD1HZW5kZXIgLA0KICAgICAgICAgICAgICB5PUN1c3RvbWVyLkxpZmV0aW1lLlZhbHVlKSkgKyBnZW9tX2JhcihzdGF0PSJzdW1tYXJ5IixmdW49Im1lYW4iLCB3aWR0aD0wLjUsIGZpbGwgPSAiQmx1ZSIpKw0KICBsYWJzKHg9IkdlbmRlciIseSA9ICJDdXN0b21lciBMaWZlIFRpbWUgVmFsdWUiLCBmaWxsPSJHZW5kZXIiKSArDQogIGdndGl0bGUoIk1lYW4gQ29udHJpYnV0aW9uIHRvIENMViBieSBHZW5kZXIiKQ0KYGBgDQpGZW1hbGVzIGFyZSBvbiBhbiBhdmVyYWdlIHNsaWdodGx5IGJldHRlciBjb250cmlidXRvcnMgdG8gQ0xWIHRoYW4gbWVuIGFzIHRoZXJlIGFyZSBtb3JlIGZlbWFsZSBzdWJzY3JpYmVycy4NCg0KDQoNCiMjIyMgVG8gdmlzdWFsaXplIHRoZSBlZmZlY3Qgb2YgU2FsZXMgQ2hhbm5lbCBvbiBDTFYuDQpgYGB7cn0NCmdncGxvdChJbnN1cmFuY2VfRGF0YXNldCxhZXMgKHg9U2FsZXMuQ2hhbm5lbCAsDQogICAgICAgICAgICAgIHk9Q3VzdG9tZXIuTGlmZXRpbWUuVmFsdWUpKSArIGdlb21fYmFyKHN0YXQ9InN1bW1hcnkiLGZ1bj0ic3VtIiwgd2lkdGg9MC41LCBmaWxsID0gIkJsdWUiKSsNCiAgbGFicyh4PSJTYWxlcyBDaGFubmVsIix5ID0gIkN1c3RvbWVyIExpZmUgVGltZSBWYWx1ZSIsIGZpbGw9IlNhbGVzIENoYW5uZWwiKSArDQogIGdndGl0bGUoIkNvbnRyaWJ1dGlvbiB0byBDTFYgYnkgU2FsZXMgQ2hhbm5lbCIpDQpgYGANCmBgYHtyfQ0KZ2dwbG90KEluc3VyYW5jZV9EYXRhc2V0LGFlcyAoeD1TYWxlcy5DaGFubmVsKSkgKyANCiAgICAgICAgIGdlb21fYmFyKHN0YXQ9ImNvdW50Iiwgd2lkdGg9MC41LCBmaWxsID0gIkJsdWUiKSArDQogIGxhYnMoeD0iUG9saWN5IFR5cGUiLHkgPSAiQ291bnQiKSArDQogIGdndGl0bGUoIkNvdW50IG9mIFNhbGVzIENoYW5uZWwiKQ0KYGBgDQoNCmBgYHtyfQ0KYWdnRGF0YSA8LSBhZ2dyZWdhdGUoeCA9IEluc3VyYW5jZV9EYXRhc2V0JEN1c3RvbWVyLkxpZmV0aW1lLlZhbHVlLA0KICAgICAgICAgICAgICAgICAgICAgYnk9bGlzdChTYWxlcy5DaGFubmVsID0gSW5zdXJhbmNlX0RhdGFzZXQkU2FsZXMuQ2hhbm5lbCksDQogICAgICAgICAgICAgICAgICAgICBGVU4gPSBtZWFuKQ0KZ2dwbG90KGRhdGEgPSBhZ2dEYXRhLCBhZXMoeCA9IFNhbGVzLkNoYW5uZWwsIHkgPSBwcm9wLnRhYmxlKHN0YXQoYWdnRGF0YSR4KSksIGZpbGwgPSBTYWxlcy5DaGFubmVsLCBsYWJlbCA9IHNjYWxlczo6cGVyY2VudChwcm9wLnRhYmxlKHN0YXQoYWdnRGF0YSR4KSkpKSkgKw0KICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKyANCiAgZ2VvbV90ZXh0KHN0YXQgPSAnaWRlbnRpdHknLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKC45KSwgIHZqdXN0ID0gLTAuNSwgc2l6ZSA9IDMpICsgDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQpICsgDQogIGxhYnMoeCA9ICdTYWxlcyBDaGFubmVsJywgeSA9ICdDTFYgaW4gUGVyY2VudGFnZScsIGZpbGwgPSAnU2FsZXMgQ2hhbm5lbCcpICsgDQogIGdndGl0bGUoIkNMViBEaXN0cmlidXRpb24gYnkgU2FsZXMgQ2hhbm5lbCIpDQpgYGANClRoZSBjdXN0b21lcnMgcHJvY3VyZWQgdGhyb3VnaCBhZ2VudHMgYXJlIGNvbnRyaWJ1dGluZyB0byBoaWdoZXIgQ0xWLg0KDQpGcm9tIHRoZSB0aGlyZCBjaGFydCwgaXQgaXMgZXZpZGVudCB0aGF0IGl0IGlzIGhhcmQgdG8gcHJlZGljdCBDTFYgZnJvbSBTYWxlcyBDaGFubmVsIGFzIGFsbCB0aGUgc3ViLWNhdGVnb3JpZXMgYXJlIGVxdWFsIGNvbnRyaWJ1dG9ycyBvbiBhbiBhdmVyYWdlIHRvIENMVi4NClRoZXJlZm9yZSB0aGUgaW5zdXJhbmNlIGNvbXBhbnkgbmVlZHMgdG8gcHJvbW90ZSB0aGUgY2hhbm5lbCB3aGljaCBjb3N0cyB0aGUgbGVhc3QgdG8gc3VzdGFpbiBvcGVyYXRpb25zLg0KDQoNCiMjIyMgVG8gdmlzdWFsaXplIHRoZSBlZmZlY3Qgb2YgVmVoaWNsZSBDbGFzcyBvbiBDTFYNCmBgYHtyfQ0KZ2dwbG90KEluc3VyYW5jZV9EYXRhc2V0LGFlcyAoeD1WZWhpY2xlLkNsYXNzICwNCiAgICAgICAgICAgICAgeT1DdXN0b21lci5MaWZldGltZS5WYWx1ZSkpICsgZ2VvbV9iYXIoc3RhdD0ic3VtbWFyeSIsZnVuPSJzdW0iLCB3aWR0aD0wLjUsIGZpbGwgPSAiQmx1ZSIpKw0KICBsYWJzKHg9IlZlaGljbGUgQ2xhc3MiLHkgPSAiQ3VzdG9tZXIgTGlmZSBUaW1lIFZhbHVlIiwgZmlsbD0iVmVoaWNsZSBDbGFzcyIpICsNCiAgZ2d0aXRsZSgiQ29udHJpYnV0aW9uIHRvIENMViBieSBWZWhpY2xlIENsYXNzIikNCmBgYA0KYGBge3J9DQpnZ3Bsb3QoSW5zdXJhbmNlX0RhdGFzZXQsYWVzICh4PVZlaGljbGUuQ2xhc3MpKSArIA0KICAgICAgICAgZ2VvbV9iYXIoc3RhdD0iY291bnQiLCB3aWR0aD0wLjUsIGZpbGwgPSAiQmx1ZSIpICsNCiAgbGFicyh4PSJWZWhpY2xlIENsYXNzIix5ID0gIkNvdW50IikgKw0KICBnZ3RpdGxlKCJDb3VudCBvZiBWZWhpY2xlIENsYXNzIikNCmBgYA0KDQpgYGB7cn0NCmFnZ0RhdGEgPC0gYWdncmVnYXRlKHggPSBJbnN1cmFuY2VfRGF0YXNldCRDdXN0b21lci5MaWZldGltZS5WYWx1ZSwNCiAgICAgICAgICAgICAgICAgICAgIGJ5PWxpc3QoVmVoaWNsZS5DbGFzcyA9IEluc3VyYW5jZV9EYXRhc2V0JFZlaGljbGUuQ2xhc3MpLA0KICAgICAgICAgICAgICAgICAgICAgRlVOID0gbWVhbikNCmdncGxvdChkYXRhID0gYWdnRGF0YSwgYWVzKHggPSBWZWhpY2xlLkNsYXNzLCB5ID0gcHJvcC50YWJsZShzdGF0KGFnZ0RhdGEkeCkpLCBmaWxsID0gVmVoaWNsZS5DbGFzcywgbGFiZWwgPSBzY2FsZXM6OnBlcmNlbnQocHJvcC50YWJsZShzdGF0KGFnZ0RhdGEkeCkpKSkpICsNCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsgDQogIGdlb21fdGV4dChzdGF0ID0gJ2lkZW50aXR5JywgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSguOSksICB2anVzdCA9IC0wLjUsIHNpemUgPSAzKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArIA0KICBsYWJzKHggPSAnVmVoaWNsZSBDbGFzcycsIHkgPSAnQ0xWIGluIFBlcmNlbnRhZ2UnLCBmaWxsID0gJ1ZlaGljbGUgQ2xhc3MnKSArIA0KICBnZ3RpdGxlKCJDTFYgRGlzdHJpYnV0aW9uIGJ5IFZlaGljbGUgQ2xhc3MiKQ0KYGBgDQpUaGVzZSB0d28gY2hhcnRzIHNob3cgdXMgdGhhdCBhbHRob3VnaCB0aGUgY3VzdG9tZXJzIG93bmluZyBMdXh1cnksIGFuZCBMdXh1cnkgU1VWIGFyZSBhIHNtYWxsIGZyYWN0aW9uLCBvbiBhbiBhdmVyYWdlIHRoZXkgY29udHJpYnV0ZSB0byBhbG1vc3QgNTAlIG9mIENMVi4gVGhlcmVmb3JlIHdlIGNhbiBtYWtlIGEgY29uY2x1c2lvbiB0aGF0IGlmIGEgY3VzdG9tZXIgb3ducyBhIEx1eHVyeSBjYXIgb3IgTHV4dXJ5IFNVViBjYXIsIHRoZXJlIGlzIGEgaGlnaCBsaWtlbGlob29kIHRoYXQgc2hlL2hlIHNoZSB3aWxsIGhhdmUgaGlnaCBDTFYuDQoNCiMjIyMgRWZmZWN0IG9uIENMViBieSBNYXJpdGFsIFN0YXR1cyBhbmQgVmVoaWNsZSBDbGFzcw0KYGBge3J9DQpwMTwtcGxvdF9seShJbnN1cmFuY2VfRGF0YXNldCwgeCA9fkluc3VyYW5jZV9EYXRhc2V0JGBNYXJpdGFsLlN0YXR1c2AsIHkgPX5JbnN1cmFuY2VfRGF0YXNldCRgQ3VzdG9tZXIuTGlmZXRpbWUuVmFsdWVgLHR5cGU9J2JhcicsY29sb3I9fkluc3VyYW5jZV9EYXRhc2V0JGBWZWhpY2xlLkNsYXNzYCkgDQpsYXlvdXQocDEsIHRpdGxlID0nQ0xWIHN0YXR1cyB3LnIudCBNYXJpdGFsIFN0YXVzICBhbmQgVmVoaWNsZSBDbGFzcycsIHlheGlzID0gbGlzdCh0aXRsZSA9ICdDTFYgJykpDQoNCg0KYGBgDQpXaGVuIHdlIGNvbnNpZGVyIE1hcml0YWwgU3RhdHVzIGFuZCBWZWhpY2xlIENsYXNzIHdlIG5vdGljZSB0aGF0IGFjcm9zcyBhbGwgdGhlIG1hcml0YWwgc3RhdHVzZXMgdGhlIGN1c3RvbWVycyBvd25pbmcgRm91ci1Eb29yIGNhcnMgYW5kIFNVVnMgYXJlIGJldHRlciBjb250cmlidXRvcnMgdG8gQ0xWLiANCg0KIyMjIyBUbyB2aXN1YWxpemUgdGhlIGVmZmVjdCBvZiBWZWhpY2xlIFNpemUgb24gQ0xWLg0KDQpgYGB7cn0NCmFnZ0RhdGEgPC0gYWdncmVnYXRlKHggPSBJbnN1cmFuY2VfRGF0YXNldCRDdXN0b21lci5MaWZldGltZS5WYWx1ZSwNCiAgICAgICAgICAgICAgICAgICAgIGJ5PWxpc3QoVmVoaWNsZS5TaXplID0gSW5zdXJhbmNlX0RhdGFzZXQkVmVoaWNsZS5TaXplKSwNCiAgICAgICAgICAgICAgICAgICAgIEZVTiA9IHN1bSkNCmdncGxvdChkYXRhID0gYWdnRGF0YSwgYWVzKHggPSBWZWhpY2xlLlNpemUsIHkgPSBwcm9wLnRhYmxlKHN0YXQoYWdnRGF0YSR4KSksIGZpbGwgPSBWZWhpY2xlLlNpemUsIGxhYmVsID0gc2NhbGVzOjpwZXJjZW50KHByb3AudGFibGUoc3RhdChhZ2dEYXRhJHgpKSkpKSArDQogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArIA0KICBnZW9tX3RleHQoc3RhdCA9ICdpZGVudGl0eScsIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoLjkpLCAgdmp1c3QgPSAtMC41LCBzaXplID0gMykgKyANCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKyANCiAgbGFicyh4ID0gJ1ZlaGljbGUgU2l6ZScsIHkgPSAnQ0xWIGluIFBlcmNlbnRhZ2UnLCBmaWxsID0gJ1ZlaGljbGUgU2l6ZScpICsgDQogIGdndGl0bGUoIkNMViBEaXN0cmlidXRpb24gYnkgVmVoaWNsZSBTaXplIikNCmBgYA0KDQoNCmBgYHtyfQ0KZ2dwbG90KEluc3VyYW5jZV9EYXRhc2V0LGFlcyAoeD1WZWhpY2xlLlNpemUpKSArIA0KICAgICAgICAgZ2VvbV9iYXIoc3RhdD0iY291bnQiLCB3aWR0aD0wLjUsIGZpbGwgPSAiQmx1ZSIpICsNCiAgbGFicyh4PSJWZWhpY2xlIFNpemUiLHkgPSAiQ291bnQiKSArDQogIGdndGl0bGUoIkNvdW50IG9mIFZlaGljbGUgU2l6ZSIpDQpgYGANCg0KDQoNCmBgYHtyfQ0KYWdnRGF0YSA8LSBhZ2dyZWdhdGUoeCA9IEluc3VyYW5jZV9EYXRhc2V0JEN1c3RvbWVyLkxpZmV0aW1lLlZhbHVlLA0KICAgICAgICAgICAgICAgICAgICAgYnk9bGlzdChWZWhpY2xlLlNpemUgPSBJbnN1cmFuY2VfRGF0YXNldCRWZWhpY2xlLlNpemUpLA0KICAgICAgICAgICAgICAgICAgICAgRlVOID0gbWVhbikNCmdncGxvdChkYXRhID0gYWdnRGF0YSwgYWVzKHggPSBWZWhpY2xlLlNpemUsIHkgPSBwcm9wLnRhYmxlKHN0YXQoYWdnRGF0YSR4KSksIGZpbGwgPSBWZWhpY2xlLlNpemUsIGxhYmVsID0gc2NhbGVzOjpwZXJjZW50KHByb3AudGFibGUoc3RhdChhZ2dEYXRhJHgpKSkpKSArDQogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArIA0KICBnZW9tX3RleHQoc3RhdCA9ICdpZGVudGl0eScsIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoLjkpLCAgdmp1c3QgPSAtMC41LCBzaXplID0gMykgKyANCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKyANCiAgbGFicyh4ID0gJ1ZlaGljbGUgU2l6ZScsIHkgPSAnQ0xWIGluIFBlcmNlbnRhZ2UnLCBmaWxsID0gJ1ZlaGljbGUgU2l6ZScpICsgDQogIGdndGl0bGUoIkNMViBEaXN0cmlidXRpb24gYnkgVmVoaWNsZSBTaXplIikNCmBgYA0KQXMgd2UgY2FuIHNlZSwgdGhlIHZhcmlhYmxlIHZlaGljbGUgc2l6ZSBpcyBhIHdlYWsgcHJlZGljdG9yIGJlY2F1c2UgYWxsIHRoZSBzdWItY2F0ZWdvcmllcyBjb250cmlidXRlIGVxdWFsbHkgdG8gdGhlIENMViBvbiBhbiBhdmVyYWdlLg0KDQoNCg0KDQpUaGUgYWJvdmUgdHJlZSBzaG93cyB1cyB0aGUgYnJlYWtkb3duIG9mIENvdmVyYWdlIHBsYW5zIGJ5IHZlaGljbGUgc2l6ZS4NCg0KIyMjIEVEQSBPRiBOVU1FUklDIERFUEVOREVOVCBWQVJJQUJMRVMgVlMgQ0xWLg0KDQojIyMjIFRvIHZpc3VhbGl6ZSB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgdmFyaWFibGVzDQpBIGNvcnJlbGF0aW9uIGhlYXQgbWFwIGlzIHBsb3R0ZWQgZm9yIGFsbCB0aGUgIG51bWVyaWMgdmFyaWFibGVzLiBUaGlzIGFsc28gY2hlY2tzIGZvciBtdWx0aS1jb2xsaW5lYXJpdHkgYmV0d2VlbiB2YXJpYWJsZXMuDQpgYGB7cn0NCmF1dG9Db3JyIDwtIEluc3VyYW5jZV9EYXRhc2V0WyxjKDMsMTAsMTM6MTcsMjIpXQ0KY29sbmFtZXMoYXV0b0NvcnIpIDwtIGMoIkN1c3RvbWVyIExpZmV0aW1lIFZhbHVlIiwgIkluY29tZSIsICJNb250aHMgUHJlbWl1bSBBdXRvIiwgIk1vbnRocyBTaW5jZSBMYXN0IENsYWltIiwgIk1vbnRocyBTaW5jZSBQb2xpY3kgSW5jZXB0aW9uIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJPcGVuIENvbXBsYWludHMiLCAiTnVtIG9mIFBvbGljaWVzIiwgIlRvdGFsIENsYWltIEFtdC4iKQ0KYXV0b0NvcnIgPC0gY29yKGF1dG9Db3JyKQ0KIyBQbG90IHRoZSBjb3JyZWxhdGlvbiB0YWJsZQ0KY29ycnBsb3QoYXV0b0NvcnIsIG1ldGhvZCA9ICJjb2xvciIsIG9yZGVyID0gImhjbHVzdCIpDQoNCmBgYA0KQXMgaXMgZXZpZGVudCBmcm9tIHRoZSBjb3JyZWxhdGlvbiBwbG90LCBNb250aGx5IFByZW1pdW0gQXV0byBhbmQgVG90YWwgQ2xhaW0gQW1vdW50IGFyZSBtb2RlcmF0ZWx5IGNvcnJlbGF0ZWQgd2hpbGUgdGhlIG90aGVyIHZhcmlhYmxlcyBhcmUgd2Vha2x5IGNvcnJlbGF0ZWQuDQpPdGhlciB0aGFuIE1vbnRobHkgUHJlbWl1bSBBdXRvIGFuZCBUb3RhbCBDbGFpbSBBbW91bnQgbmVnbGlnaWJsZSBtdWx0aWNvbGxpbmVhcml0eSBpcyBzZWVuIGJldHdlZW4gdGhlIHJlbWFpbmluZyBpbmRlcGVuZGVudCB2YXJpYWJsZXMuIA0KDQoNCg0KIyMjIyBUbyBleHBsb3JlIHRoZSBlZmZlY3Qgb2YgSW5jb21lIGFuZCBUb3RhbCBDbGFpbSBBbW91bnQNCg0KYGBge3J9DQpwbG90KHg9SW5zdXJhbmNlX0RhdGFzZXQkIkluY29tZSIsIHk9SW5zdXJhbmNlX0RhdGFzZXQkIlRvdGFsLkNsYWltLkFtb3VudCIsIGNvbD0iQmx1ZSIsIGNleD0xLCB4bGFiPSJJbmNvbWUiLA0KICAgICB5bGFiPSJUb3RhbCBDbGFpbSBBbW91bnQiLG1haW49IlNjYXR0ZXJwbG90IG9mIEluY29tZSB2cyBUQ0EiKQ0KYGBgDQpXZSBzZWUgaW4gdGhpcyBjaGFydCB0aGF0IHRoZXJlIGlzIG5vIGxpbmVhciBwb3NpdGl2ZSBvciBuZWdhdGl2ZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB2YXJpYWJsZXMuDQpUaGlzIG1lYW5zIHRoYXQgdGhleSBhcmUgaW5kZXBlbmRlbnQgb2YgZWFjaCBvdGhlcg0KDQoNCiMjIyMgVG8gdmlzdWFsaXplIHRoZSBlZmZlY3Qgb2YgTW9udGhseSBQcmVtaXVtIEF1dG8gYW5kIFRvdGFsIENsYWltIEFtb3VudA0KDQpgYGB7cn0NCnBsb3QoeD1JbnN1cmFuY2VfRGF0YXNldCQiTW9udGhseS5QcmVtaXVtLkF1dG8iLCB5PUluc3VyYW5jZV9EYXRhc2V0JCJUb3RhbC5DbGFpbS5BbW91bnQiLCBjb2w9IkJsdWUiLCBjZXg9MSwgeGxhYj0iTW9udGhseSBQcmVtaXVtIEF1dG8iLA0KICAgICB5bGFiPSJUb3RhbCBDbGFpbSBBbW91bnQiLG1haW49IlNjYXR0ZXJwbG90IG9mIE1QQSB2cyBUQ0EiKQ0KYGBgDQpIZXJlIHdlIHNlZSB0aGUgIHJlbGF0aW9uc2hpcCBvZiBNUEEgYW5kIFRDQSwgd2Ugbm90aWNlIHRoYXQgYSBmZXcgY2x1c3RlcnMgaGF2ZSBhIHBvc2l0aXZlIGxpbmVhciByZWxhdGlvbnNoaXAsIGFzIGV2aWRlbmNlZCBieSB0aGUgdXB3YXJkIHNsb3BlcyBpbiB0aGUgY2hhcnQuIA0KDQojIyMjIFRvIHZpc3VhbGl6ZSB0aGUgZWZmZWN0IG9mIE1vbnRobHkgUHJlbWl1bSBBdXRvIGFuZCBDTFYNCmBgYHtyfQ0KcGxvdCh4PUluc3VyYW5jZV9EYXRhc2V0JCJNb250aGx5LlByZW1pdW0uQXV0byIsIHk9SW5zdXJhbmNlX0RhdGFzZXQkIkN1c3RvbWVyLkxpZmV0aW1lLlZhbHVlIiwgY29sPSJCbHVlIiwgY2V4PTEsIHhsYWI9Ik1vbnRobHkgUHJlbWl1bSBBdXRvIiwNCiAgICAgeWxhYj0iQ3VzdG9tZXIgTGlmZXRpbWUgVmFsdWUiLG1haW49IlNjYXR0ZXJwbG90IG9mIE1QQSB2cyBDTFYiKQ0KYGBgDQpGcm9tIHRoZSBzY2F0dGVycGxvdCBpdCBpcyBldmlkZW50IHRoYXQgaGlnaGVyIHRoZSBNUEEsIGhpZ2hlciBpcyB0aGUgQ0xWLg0KDQoNCg0KDQojIyMjIFRvIHZpc3VhbGl6ZSB0aGUgZWZmZWN0IG9mIFRvdGFsIENsYWltIEFtb3VudCBvbiBDTFYuDQpgYGB7cn0NCnBsb3QoeD1JbnN1cmFuY2VfRGF0YXNldCQiVG90YWwuQ2xhaW0uQW1vdW50IiwgeT1JbnN1cmFuY2VfRGF0YXNldCQiQ3VzdG9tZXIuTGlmZXRpbWUuVmFsdWUiLCBjb2w9IkJsdWUiLCBjZXg9MSwgeGxhYj0iVG90YWwuQ2xhaW0uQW1vdW50IiwgDQogICAgIHlsYWI9IkN1c3RvbWVyIExpZmV0aW1lIFZhbHVlIiwgbWFpbj0iU2NhdHRlcnBsb3Qgb2YgVENBIHZzIENMViIpDQpgYGANClRoZXJlIGlzIG5vIGV2aWRlbmNlIHRoYXQgdGhlcmUgaXMgYW55IGxpbmVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiBUb3RhbCBDbGFpbSBBbW91bnQgYW5kIENMViBhcyB0aGUgc2NhdHRlcnBsb3QgaXMgaW5jb25jbHVzaXZlLiBUaGVyZSBpcyBubyBjbGVhciBzbG9wZSBlaXRoZXIgZG93bndhcmQgb3IgdXB3YXJkIGluIHRoZSBjaGFydCBpbmRpY2F0aW5nIHRoYXQgdGhlc2UgdHdvIHZhcmlhYmxlcyBhcmUgaW5kZXBlbmRlbnQgb2YgZWFjaCBvdGhlci4NCg0KDQojIyBGZWF0dXJlIEVuZ2luZWVyaW5nLCBGZWF0dXJlIFNlbGVjdGlvbiBhbmQgTW9kZWwgQnVpbGRpbmcNCg0KIyMjICBJbnRyb2R1Y3Rpb24gIA0KDQpIYXZpbmcgZG9uZSB0aGUgRURBLCBGZWF0dXJlIEVuZ2luZWVyaW5nLCBGZWF0dXJlIFNlbGVjdGlvbiBhbmQgTW9kZWwgQnVpbGRpbmcgd2FzIGNhcnJpZWQgb3V0Lg0KDQojIyMgRmVhdHVyZSBFbmdpbmVlcmluZzoNCg0KMS4JU3FydCBhbmQgTG9nIHRyYW5zZm9ybWF0aW9ucyBoYXZlIGJlZW4gdXNlZCBpbiB0cnlpbmcgb3V0IHZhcmlvdXMgbW9kZWxzLg0KDQoyLglWYXJpYXRpb25zIG9mIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIOKAnE1vbnRobHkgUHJlbWl1bSBBdXRv4oCdIGFuZCDigJxOdW1iZXIgb2YgUG9saWNpZXPigJ0gd2VyZSB0cmllZCBvdXQgYnV0IHRoZWlyIGVmZmVjdCB3YXMgcmVkdW5kYW50Lg0KDQojIyMgRmVhdHVyZSBTZWxlY3Rpb24NCg0KRmVhdHVyZSBzZWxlY3Rpb24gdXNpbmcgU3RlcHdpc2UgUmVncmVzc2lvbiwgUmFuZG9tIEZvcmVzdCBhbmQgQU5PVkEgd2VyZSBjYXJyaWVkIG91dC4gVGhlc2UgZmVhdHVyZXMgd2VyZSB1c2VkIGluIHRoZSB2YXJpb3VzIG1vZGVscyB0aGF0IHdlcmUgdHJpZWQgb3V0LCB3aXRoIGFuZCB3aXRob3V0IHRyYW5zZm9ybWF0aW9uLiBUaGUgZGV0YWlscyBhcmUgaW4gdGhlIHJlcG9ydCBiZWxvdy4NCg0KDQoNCmBgYHtyIHByZXNzdXJlMSwgZWNobz1GQUxTRSwgZmlnLmNhcD0iRmVhdHVyZSBTZWxlY3Rpb24iLCBvdXQud2lkdGggPSAnMTAwJSd9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiMS5qcGVnIikNCg0KYGBgDQoNCg0KDQoNCiMjIyBNb2RlbCBCdWlsZGluZw0KDQojIyMjIDEuIFJhbmRvbSBGb3Jlc3QuDQpPbmUgb2YgdGhlIG1vZGVscyB0aGF0IHdhcyBjaG9zZW4gd2FzIFJhbmRvbSBGb3Jlc3QuIFNpbmNlIHRoZSBkYXRhIGhhZCBhIGxvdCBvZiBvdXRsaWVycywgcmFuZG9tIGZvcmVzdCB3YXMgc2VsZWN0ZWQgYXMgaXQgaXMgcmVzaWxpZW50IHRvIG91dGxpZXJzLiBSYW5kb20gRm9yZXN0IGFsZ29yaXRobSBpdHNlbGYgaXMgbm90IHJvYnVzdCB0byBvdXRsaWVycyBidXQgdGhlIGJhc2UgbGVhcm5lciBvbiB3aGljaCBpdCBpcyBidWlsdCAtIHRoZSBkZWNpc2lvbiB0cmVlLCBpcy5UaGUgUjIgdmFsdWUgKGluIHBlcmNlbnQpIGFuZCBBZGp1c3RlZCBSMiB2YWx1ZXMgKGluIHBlcmNlbnQpIG9mIHRoZSBSRiBtb2RlbCBmb3IgYWxsIHRoZSB2YXJpb3VzIHRyaWFscyB3aXRoIGRpZmZlcmVudCB2YXJpYWJsZXMgd2FzIDk2JSBvciBoaWdoZXIsIGFzIHNob3duIGluIHRoZSB0YWJsZSBiZWxvdzotDQoNCg0KDQoNCg0KYGBge3IgcHJlc3N1cmUyLCBlY2hvPUZBTFNFLCBmaWcuY2FwPSJTdW1tYXJ5ICIsIG91dC53aWR0aCA9ICcxMDAlJ30NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCIyLkpQRUciKQ0KDQpgYGANCg0KQmFzZWQgb24gcmVzZWFyY2ggY2FycmllZCBvdXQgb24gdGhlIGludGVybmV0IGZvciB0aGlzIG1vZGVsLCBSYW5kb20gRm9yZXN0IGlzIGJpYXNlZCB0b3dhcmRzIHNwZWNpZmljIGZhY3RvcnMgKGxpa2UgY2F0ZWdvcmljYWwgdmFyaWFibGVzIHdpdGggZGlmZmVyZW50IGxldmVscykgYmVjYXVzZSBpdCBwcm92aWRlZCBleGNlcHRpb25hbGx5IGhpZ2ggcmVzdWx0cyBmb3IgZWFjaCBleHBlcmltZW50LCB3aGljaCBkb2VzIG5vdCBzZWVtIHJlYWxpc3RpYy4gQXMgYSByZXN1bHQsIGl0IHdhcyBkZWNpZGVkIHRvIG5vdCBnbyBhaGVhZCB3aXRoIFJhbmRvbSBGb3Jlc3QuDQoNCiogSW4gdHJpYWwgMiwgc29tZSBvZiB0aGUgdmFyaWFibGVzIHdlcmUgbm90IGluY2x1ZGVkIHdoaWNoIGFyZSBDdXN0b21lciwgU3RhdGUsIFJlc3BvbnNlLCBFZmZlY3RpdmUgdG8gRGF0ZSwgSW5jb21lLCBQb2xpY3kgVHlwZSBhbmQgSW5jb21lX0Jpbi4gVGhlIHNlbGVjdGlvbiBvZiB0aGVzZSB2YXJpYWJsZXMgd2FzIHRocm91Z2ggdHJpYWwgYW5kIGVycm9yIGFuZCB3ZXJlIHNlbGVjdGVkIGFzIHRoZXNlIHdlcmUgZ2l2aW5nIGJldHRlciByZXN1bHRzLg0KDQoNCiMjIyMgMi4JTGluZWFyIFJlZ3Jlc3Npb24uCQ0KDQpUaGUgc2Vjb25kIG1vZGVsIHdhcyBMaW5lYXIgUmVncmVzc2lvbi4gVGhlIHN1bW1hcnkgb2YgdGhlIHZhcmlvdXMgbW9kZWxzIHRyaWVkIG91dCBpcyBhcyBnaXZlbiBiZWxvdyA6LQ0KDQoNCg0KIyMjIyMgICAgICAgICAgICAgICAgICAgICBTVU1NQVJZIDogUkVHUkVTU0lPTg0KDQpgYGB7ciBwcmVzc3VyZTMsIGVjaG89RkFMU0UsIGZpZy5jYXA9Ik5vdCBTY2FsZWQiLCBvdXQud2lkdGggPSAnMTAwJSd9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiMy5KUEVHIikNCg0KYGBgDQoNCg0KDQpgYGB7ciBwcmVzc3VyZTQsIGVjaG89RkFMU0UsIGZpZy5jYXA9IlNjYWxlZCIsIG91dC53aWR0aCA9ICcxMDAlJ30NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCI0LkpQRUciKQ0KDQpgYGANCiMjICAgICAgICAgICAgICAgICAgICAgICAgICAgIENPTkNMVVNJT05TDQoNCg0KVmFyaW91cyBtb2RlbHMgd2VyZSB0cmllZCBvdXQgYW5kIHRoZWlyIHBlcmZvcm1hbmNlIG1lYXN1cmVzIHRhYnVsYXRlZCBhcyBnaXZlbiBhYm92ZS4gVGhlIG1vZGVscyB3ZXJlIG1vZGVsbGVkIHdpdGhvdXQgZm9sbG93aW5nIGZlYXR1cmUgc2VsZWN0aW9uIG1ldGhvZHMsIGluaXRpYWxseSwgIGFuZCwgbGF0ZXIsIHVzaW5nIGZlYXR1cmUgc2VsZWN0aW9uIG1ldGhvZHMuIEEgc3VtbWFyeSBvZiB0aGUgZmVhdHVyZXMgc2VsZWN0ZWQgaW4gdmFyaW91cyBtb2RlbHMgaXMgYXMgc2hvd24gaW4gYSBzdWJzZXF1ZW50IHNlY3Rpb24uDQoNCiMjIyMgV2l0aG91dCBGZWF0dXJlIHNlbGVjdGlvbiBhbmQgYW55IGZlYXR1cmUgZW5naW5lZXJpbmcgOi0NCg0KMS4JSWYgb3V0bGllcnMgYXJlIG5vdCByZW1vdmVkLCB0aGVuIHRoZSBwZXJmb3JtYW5jZSBpcyBleHRyZW1lbHkgcG9vciwgZXZlbiBhZnRlciByZW1vdmluZyB0aGUgbGVhc3Qgc2lnbmlmaWNhbnQgZmVhdHVyZXMuDQoNCjIuCUlmIG91dGxpZXJzIGFyZSByZW1vdmVkIGZyb20gdGhlIGluZGljYXRvciB2YXJpYWJsZXMsIHRoZW4gdGhlIHBlcmZvcm1hbmNlIG9mIEFkaiBSMmltcHJvdmVzIHRvIGFwcHJveC4gNjMlLiBUaGVyZSBpcyBoYXJkbHkgYW55IGVmZmVjdCBpbiB0aGUgQWRqIFIyICB2YWx1ZSB3aGVuIGluc2lnbmlmaWNhbnQgdmFyaWFibGVzIGFyZSByZW1vdmVkIGFuZCBldmVuIGFmdGVyIGJpbm5pbmcgaXMgZWZmZWN0ZWQuIFJlbW92YWwgb2Ygb3V0bGllcnMgcmVzdWx0cyBpbiByZW1vdmFsIG9mIGFwcHJveC4gOCUgb2YgdGhlIHJlY29yZHMgYmVpbmcgcmVtb3ZlZC4NCg0KMy4JV2hlbiBvdXRsaWVycyBhcmUgcmVtb3ZlZCBmcm9tIHRoZSByZXNwb25kZW50IHZhcmlhYmxlLCBjbHYsIHRoZSBwZXJmb3JtYW5jZSBkcmFtYXRpY2FsbHkgaW1wcm92ZXMgdG8gOTMlLiBUaGVyZSBpcyBubyBjaGFuZ2UgaW4gdGhpcyBmaWd1cmUgZXZlbiBhZnRlciBiaW5uaW5nLiBSZW1vdmFsIG9mIG91dGxpZXJzIHJlc3VsdHMgaW4gcmVtb3ZhbCBvZiBhcHByb3guIDEyJSBvZiB0aGUgcmVjb3JkcyBiZWluZyByZW1vdmVkLg0KDQo0LglXaGVuIG91dGxpZXJzIGFyZSByZW1vdmVkIGZyb20gdGhlIHJlc3BvbmRlbnQgdmFyaWFibGUsIGNsdiwgdGhlcmUgYXJlIG91dGxpZXJzIHN0aWxsIGF2YWlsYWJsZSBpbiBUb3RhbCBDbGFpbSBBbW91bnQgYW5kIE1vbnRobHkgUHJlbWl1bSBBdXRvLiBSZW1vdmFsIG9mIG91dGxpZXJzIGZyb20gdGhlc2UgZmVhdHVyZXMgcmVzdWx0cyBpbiByZW1vdmFsIG9mIGEgdG90YWwgb2YgMTYuODglIG9mIHJlY29yZHMuIFRoZSBwZXJmb3JtYW5jZSwgaG93ZXZlciwgdmFyaWVzIG9ubHkgZnJvbSB0aGUgdGhpcmQgZGVjaW1hbCBwb2ludCBvbndhcmRzIHdydCB0aGUgY2FzZSBpbiBwb2ludCAzIGFib3ZlLCBhbmQgaGVuY2UgdGhlcmUgaXMgbWFyZ2luYWwgaW1wcm92ZW1lbnQgaW4gcGVyZm9ybWFuY2Ugd2l0aCBhbGwgb3V0bGllcnMgcmVtb3ZlZC4gDQoNCjUuCUhvd2V2ZXIsIHN0ZXBzIDMgYW5kIDQgd2VyZSBvbmx5IHRvIGNoZWNrIHRoZSBlZmZlY3Qgb2YgcmVtb3Zpbmcgb3V0bGllcnMgZnJvbSB0aGUgcmVzcG9uZGVudCB2YXJpYWJsZS4gVGhpcyBpcyBub3QgYmVpbmcgZm9sbG93ZWQuDQoNCg0KIyMjIyBXaXRoIEZlYXR1cmUgU2VsZWN0aW9uIGFuZC9vciBzcXJ0L2xvZyB0cmFuc2Zvcm1hdGlvbjoNCg0KMS4JVGhlIEFkaiBSMiB2YWx1ZSByZW1haW5zIGFyb3VuZCA2MyUgaXJyZXNwZWN0aXZlIG9mIHdoZXRoZXIgb3V0bGllcnMgYXJlIHJlbW92ZWQgb3Igbm90IGFuZCB3aGV0aGVyIGJpbm5pbmcgaXMgZG9uZSBvciBub3QuVGhlcmVmb3JlLCB3ZSByZXRhaW4gYWxsIG91dGxpZXJzIGFzIGluZGljYXRvcnMgb2YgdmFyaWF0aW9uIGluIHRoZSBkYXRhIGFuZCBkbyBub3QgY2Fycnkgb3V0IGJpbm5pbmcuIEluc3RlYWQsIHdlIGNhcnJ5IG91dCBzY2FsaW5nIG9mIHRoZSBkYXRhLg0KDQoyLglXaGVuIHdlIGFwcGx5IHNxcnQgdHJhbnNmb3JtYXRpb24gdG8gdGhlIHJlc3BvbmRlbnQgdmFyaWFibGUgdGhlIEFkaiBSMiB2YWx1ZSBnb2VzIHVwIHRvIDc5IOKAkyA4MCUgYXBwcm94Lg0KDQozLglXaGVuIHdlIGFwcGx5IGxvZyB0cmFuc2Zvcm1hdGlvbiB0byB0aGUgcmVzcG9uZGVudCB2YXJpYWJsZSwgd2Ugb2J0YWluIEFkaiBSMiB2YWx1ZXMgaW4gdGhlIHJlZ2lvbiBvZiA4OS05MCUuDQoNCjQuCVRoZSBiZXN0IHZhbHVlcyBhcmUgb2J0YWluZWQgaW4gdGhlIG1vZGVsIHdoZXJlIGFsbCB0aGUgZmVhdHVyZXMgYXJlIHRha2VuIGFuZCB0aGUgbG9nIHRyYW5zZm9ybWF0aW9uIGlzIGFwcGxpZWQgdG8gdGhlIHJlc3BvbmRlbnQgdmFyaWFibGUuDQoNCjUuCeKAnE1vbnRobHkgUHJlbWl1bSBBdXRv4oCdIHdhcyBvbWl0dGVkIGZyb20gbW9kZWxsaW5nIGR1ZSB0byBpdHMgY29ycmVsYXRpb24gd2l0aCDigJxUb3RhbCBDbGFpbSBBbW91bnTigJ0uDQoNCg0KIyMjIyBQZXJ0aW5lbnQgVGFrZS1hd2F5czoNCg0KMS4JQmlubmluZyBoYXMgbmVnbGlnaWJsZSBlZmZlY3Qgb24gcGVyZm9ybWFuY2UuDQoNCjIuCVRyYW5zZm9ybWF0aW9uIG9mIFJlc3BvbmRlbnQgdmFyaWFibGUgKHNxcnQvTG9nKSBoYXMgYSBzaWduaWZpY2FudCBpbXByb3ZlbWVudCBpbiBwZXJmb3JtYW5jZSB2aXMtw6AtdmlzIHRoZSBub24gdHJhbnNmb3JtZWQgdmFyaWFudHMuDQoNCjMuCVJlbW92YWwgb2Ygb3V0bGllcnMgaW1wcm92ZWQgcGVyZm9ybWFuY2UsIGJ1dCBhbHNvIGNhdXNlZCBzaWduaWZpY2FudCBsb3NzIG9mIGRhdGEuIEhlbmNlLCBvdXRsaWVycyB3ZXJlIHJldGFpbmVkLg0KDQo0LglSZW1vdmFsIG9mIGxlYXN0IHNpZ25pZmljYW50IGZlYXR1cmVzIGhhcmRseSBjYXVzZWQgYW4gaW1wcm92ZW1lbnQgaW4gcGVyZm9ybWFuY2UuIEhlbmNlLCBmZWF0dXJlIHNlbGVjdGlvbiB0ZWNobmlxdWVzIHdlcmUgZW1wbG95ZWQuDQoNCjUuCUNvbnZlcnRpbmcg4oCcTnVtYmVyIG9mIE9wZW4gQ29tcGxhaW50c+KAnSBhbmQg4oCcTnVtYmVyIG9mIFBvbGljaWVz4oCdIHRvIGZhY3RvcnMgaW1wcm92ZWQgYWNjdXJhY3kgb2YgdGhlIG1vZGVscy4NCg0KDQojIyMgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEJlc3QgTW9kZWwNCg0KVGhlIGJlc3QgbW9kZWwgd2FzIG9uZSB3aGljaCB1c2VkIGFsbCB0aGUgZmVhdHVyZXMgYW5kIGhhZCB0aGUgbG9nIHRyYW5zZm9ybWF0aW9uIGFwcGxpZWQgdG8gdGhlIHJlc3BvbmRlbnQgdmFyaWFibGUuDQoNCg0KDQoNCiMjIyMgQ29kZSBmb3IgdGhlIG1vZGVsIHdpdGggdGhlIGJlc3QgcGVyZm9ybWFuY2UNCg0KYGBge3J9DQoNCg0KZGY8LSByZWFkLmNzdigiQzpcXFVzZXJzXFxIUFxcRG93bmxvYWRzXFxNYXJrZXRpbmctQ3VzdG9tZXItVmFsdWUtQW5hbHlzaXMuY3N2IikNCnN0cihkZikNCmdsaW1wc2UoZGYpDQpgYGANCg0KDQpgYGB7cn0NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLU1pbiBNYXggbm9ybWFsaXphdGlvbiBhbGwgbnVtZXJpYyB2YXJpYWJsZXMgKFNjYWxpbmcpLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIw0KDQojSW5jb21lDQpkZiRJbmNvbWU8LSAoZGYkSW5jb21lLW1pbihkZiRJbmNvbWUpKS8obWF4KGRmJEluY29tZSktbWluKGRmJEluY29tZSkpDQpgYGANCg0KDQoNCg0KDQpgYGB7cn0NCiNNb250aHMuU2luY2UuTGFzdC5DbGFpbQ0KZGYkTW9udGhzLlNpbmNlLkxhc3QuQ2xhaW08LSAoZGYkTW9udGhzLlNpbmNlLkxhc3QuQ2xhaW0tbWluKGRmJE1vbnRocy5TaW5jZS5MYXN0LkNsYWltKSkvKG1heChkZiRNb250aHMuU2luY2UuTGFzdC5DbGFpbSktbWluKGRmJE1vbnRocy5TaW5jZS5MYXN0LkNsYWltKSkNCmBgYA0KDQoNCmBgYHtyfQ0KI01vbnRocy5TaW5jZS5Qb2xpY3kuSW5jZXB0aW9uDQpkZiRNb250aHMuU2luY2UuUG9saWN5LkluY2VwdGlvbjwtIChkZiRNb250aHMuU2luY2UuUG9saWN5LkluY2VwdGlvbi1taW4oZGYkTW9udGhzLlNpbmNlLlBvbGljeS5JbmNlcHRpb24pKS8obWF4KGRmJE1vbnRocy5TaW5jZS5Qb2xpY3kuSW5jZXB0aW9uKS1taW4oZGYkTW9udGhzLlNpbmNlLlBvbGljeS5JbmNlcHRpb24pKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCiNUb3RhbC5DbGFpbS5BbW91bnQNCmRmJFRvdGFsLkNsYWltLkFtb3VudDwtIChkZiRUb3RhbC5DbGFpbS5BbW91bnQtbWluKGRmJFRvdGFsLkNsYWltLkFtb3VudCkpLyhtYXgoZGYkVG90YWwuQ2xhaW0uQW1vdW50KS1taW4oZGYkVG90YWwuQ2xhaW0uQW1vdW50KSkNCg0KYGBgDQoNCg0KYGBge3J9DQojY29udmVydGluZyBjYXRlZ29yaWNhbCBmZWF1dGVycyB0byBmYWN0b3JzLg0KZGYkU3RhdGUgPC0gYXMuZmFjdG9yKGRmJFN0YXRlKQ0KZGYkUmVzcG9uc2UgPC0gYXMuZmFjdG9yKGRmJFJlc3BvbnNlKQ0KZGYkQ292ZXJhZ2UgPC0gYXMuZmFjdG9yKGRmJENvdmVyYWdlKQ0KZGYkRWR1Y2F0aW9uIDwtIGFzLmZhY3RvcihkZiRFZHVjYXRpb24pDQpkZiRFbXBsb3ltZW50U3RhdHVzIDwtIGFzLmZhY3RvcihkZiRFbXBsb3ltZW50U3RhdHVzKQ0KZGYkR2VuZGVyIDwtIGFzLmZhY3RvcihkZiRHZW5kZXIpDQpkZiRMb2NhdGlvbi5Db2RlICA8LSBhcy5mYWN0b3IoZGYkTG9jYXRpb24uQ29kZSkNCmRmJE1hcml0YWwuU3RhdHVzICA8LSBhcy5mYWN0b3IoZGYkTWFyaXRhbC5TdGF0dXMpDQpkZiRQb2xpY3kuVHlwZSAgPC0gYXMuZmFjdG9yKGRmJFBvbGljeS5UeXBlKQ0KZGYkUmVuZXcuT2ZmZXIuVHlwZSAgPC0gYXMuZmFjdG9yKGRmJFJlbmV3Lk9mZmVyLlR5cGUpDQpkZiRQb2xpY3kgIDwtIGFzLmZhY3RvcihkZiRQb2xpY3kpDQpkZiRTYWxlcy5DaGFubmVsICA8LSBhcy5mYWN0b3IoZGYkU2FsZXMuQ2hhbm5lbCkNCmRmJFZlaGljbGUuQ2xhc3MgIDwtIGFzLmZhY3RvcihkZiRWZWhpY2xlLkNsYXNzKQ0KZGYkVmVoaWNsZS5TaXplICA8LSBhcy5mYWN0b3IoZGYkVmVoaWNsZS5TaXplKQ0KYGBgDQoNCmBgYHtyfQ0KI0NvbnZlcnRpbmcgbm8uIG9mIG9wZW4gY29tcGxhaW50cyBhbmQgcG9saWNpZXMgYWxzbyB0byBmYWN0b3IuDQpkZiROdW1iZXIub2YuT3Blbl9Db21wbGFpbnRzIDwtIGFzLmZhY3RvcihkZiROdW1iZXIub2YuT3Blbi5Db21wbGFpbnRzKQ0KZGYkTnVtYmVyLm9mLlBvbGljaWVzIDwtIGFzLmZhY3RvcihkZiROdW1iZXIub2YuUG9saWNpZXMpDQpzdHIoZGYpDQpgYGANCg0KDQpgYGB7cn0NCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLUxvZyB0cmFuc2Zvcm1hdGlvbiBvbmx5IG9uIENMVi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSMNCg0KDQpkZiRDdXN0b21lci5MaWZldGltZS5WYWx1ZT1sb2coZGYkQ3VzdG9tZXIuTGlmZXRpbWUuVmFsdWUpDQpgYGANCg0KDQoNCg0KYGBge3J9DQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1TcGxpdHRpbmcgdGhlIGRhdGEgdG8gdGVzdCBhbmQgdHJhaW4uLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIw0KDQoNCnNwbGl0IDwtIHNhbXBsZS5zcGxpdChkZiwgU3BsaXRSYXRpbyA9IDAuNykNCnNwbGl0DQp0cmFpbiA8LSBzdWJzZXQoZGYsIHNwbGl0PSJ0cnVlIikNCnRlc3QgPC1zdWJzZXQoZGYsIHNwbGl0PSJmYWxzZSIpDQp0cmFpbg0KYGBgDQoNCmBgYHtyfQ0KI1RyYWluaW5nIHRoZSBtb2RlbCB3aXRoIG5vcm1hbGl6ZWQgZGF0YSBjb2x1bW5zIGFuZCBsb2cgdHJhbnNmb3JtZWQgY2x2DQpmaXQzPC0gbG0oQ3VzdG9tZXIuTGlmZXRpbWUuVmFsdWUgfiAJU3RhdGUrUmVzcG9uc2UrQ292ZXJhZ2UrDQogICAgICAgICAgIEVkdWNhdGlvbitFbXBsb3ltZW50U3RhdHVzK0dlbmRlcisNCiAgICAgICAgICAgSW5jb21lK0xvY2F0aW9uLkNvZGUrTWFyaXRhbC5TdGF0dXMrDQogICAgICAgICAgIE1vbnRocy5TaW5jZS5MYXN0LkNsYWltK01vbnRocy5TaW5jZS5Qb2xpY3kuSW5jZXB0aW9uKw0KICAgICAgICAgICBOdW1iZXIub2YuT3Blbi5Db21wbGFpbnRzK051bWJlci5vZi5Qb2xpY2llcytQb2xpY3krICAgICAgICAgICAgUmVuZXcuT2ZmZXIuVHlwZStTYWxlcy5DaGFubmVsK1RvdGFsLkNsYWltLkFtb3VudCtWZWhpY2xlLkNsYXNzK1ZlaGljbGUuU2l6ZSAsIGRhdGE9dHJhaW4pDQoNCnN1bW1hcnkoZml0MykNCmBgYA0KYGBge3J9DQojRmluZGluZyBSU0UNCnNpZ21hKGZpdDMpDQpgYGANCg0KYGBge3J9DQojIGNvbXB1dGluZyB0ZXN0IE1TRQ0KdGVzdCAlPiUgDQogIGFkZF9wcmVkaWN0aW9ucyhmaXQzKSAlPiUNCiAgc3VtbWFyaXNlKE1TRSA9IG1lYW4oKEN1c3RvbWVyLkxpZmV0aW1lLlZhbHVlIC0gcHJlZCleMikpDQpgYGANCg0KDQoNCmBgYHtyfQ0KIyBjb21wdXRpbmcgdHJhaW4gTVNFDQp0cmFpbiAlPiUgDQogIGFkZF9wcmVkaWN0aW9ucyhmaXQzKSAlPiUNCiAgc3VtbWFyaXNlKE1TRSA9IG1lYW4oKEN1c3RvbWVyLkxpZmV0aW1lLlZhbHVlIC0gcHJlZCleMikpDQpgYGANCg0KYGBge3J9DQojUGxvdHRpbmcgdGhlIG1vZGVsLg0KcGxvdChmaXQzKQ0KYGBgDQoNCg0KYGBge3J9DQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIENoZWNraW5nIG9mIEFzc3VtcHRpb24gIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KDQojIDEuIEluIHRoZSByZXNpZHVhbCB2cyBmaXR0ZWQgZ3JhcGggd2UgY2Fubm90IHNlZSBhbnkgZnVubmVsIHNoYXBlIGluIHRoZSByZXNpZHVlcywgaGVuY2UgdGhlIGFzc3VtcHRpb24gb2YgaG9tb3NrZWRhc3RpY2l0eSBpcyBzYXRpc2ZpZWQuDQoNCiMgMi4gVGhlIHBvaW50cyBpbiB0aGUgY2VudGVyIHBhcnQgb2YgdGhlIGdyYXBocyBmb2xsb3cgdGhlIFEtUSBwbG90LiBUaGUgdHJhaWxpbmcgcG9ydGlvbiBkZXZpYXRlcyBmcm9tIHRoZSBRLVEgcGxvdCBieSBhIHNtYWxsIGFtb3VudC4gSG93ZXZlciwgdGhlIGxlYWRpbmcgcG9ydGlvbiBkZXZpYXRlcyBzaWduaWZpY2FudGx5IGZyb20gdGhlIFEtUSBwbG90IGluZGljYXRpbmcgbm9uIGFkaGVyZW5jZSB0byBub3JtYWxpdHkuIFRoZXJlZm9yZSwgTG9nIHRyYW5zZm9ybWF0aW9uIGhhcyBiZWVuIGFwcGxpZWQgdG8gdGhlIHRhcmdldCB2YXJpYWJsZS4gVGhlIGdyYXBoIG9mIHRoZSBsb2cgdHJhbnNmb3JtZWQgdGFyZ2V0IHZhcmlhYmxlIGlzIGRpc3BsYXllZCBiZWxvdy4gQXMgd2UgY2FuIHNlZSBncmFwaCByZXNlbWJsZXMgdGhlIG5vcm1hbCBjdXJ2ZS4NCg0KIyAzLiBSZXNpZHVhbHMgYXJlIHNwcmVhZCBlcXVhbGx5IGFsb25nIHRoZSByYW5nZXMgb2YgcHJlZGljdG9ycywgaW5kaWNhdGluZyBob21vc2NlZGFzdGljaXR5LiBXZSBjYW4gc2VlIGEgIGhvcml6b250YWwgbGluZSB3aXRoIGVxdWFsbHkgKHJhbmRvbWx5KSBzcHJlYWQgcG9pbnRzLg0KDQojIDQuIEV2ZW4gdGhvdWdoIHRoZXJlIHNlZW1zIHRvIGJlIGV4dHJlbWUgdmFsdWVzLCB0aGUgcmVncmVzc2lvbiBsaW5lIGlzIG1vcmUgb3IgbGVzcyBzdHJhaWdodC4gIA0KYGBgDQoNCg0KYGBge3J9DQojIFBsb3Qgb2YgTG9nIHRyYW5zZm9ybWVkIENMViANCmhpc3QoZGYkQ3VzdG9tZXIuTGlmZXRpbWUuVmFsdWUsDQpicmVha3MgPSA4MDAsDQpmcmVxID0gRkFMU0UsDQptYWluID0gIkNMViBIaXN0b2dyYW0iLCB4bGFiID0gIkNMViIsIGJvcmRlciA9ICJCbHVlIikNCmBgYA0KDQoNCmBgYHtyfQ0KDQoNCiMgUmVzaWR1YWxzIHNob3VsZCBiZSB1bmNvcnJlbGF0ZWQuVGhlcmUgc2hvdWxkIGJlIG5vIEF1dG9jb3JyZWxhdGlvbi4NCiMgTnVsbCBIMDogcmVzaWR1YWxzIGZyb20gYSBsaW5lYXIgcmVncmVzc2lvbiBhcmUgdW5jb3JyZWxhdGVkLiANCiMgRC1XIFN0YXRpc3RpYyBzaG91bGQgYmUgY2xvc2UgdG8gMi4gDQoNCg0KZHVyYmluV2F0c29uVGVzdChmaXQzKQ0KDQoNCiNTaW5jZSwgdGhlIHAtdmFsdWUgaXMgPjAuMDUsIHdlIGZhaWwgdG8gcmVqZWN0IEgwOiAoTm8gQXV0b2NvcnJlbGF0aW9uKQ0KYGBgDQoNCg0KDQoNCg0KYGBge3J9DQojIENoZWNraW5nIG11bHRpY29sbGluZWFyaXR5DQoNCnZpZihmaXQzKQ0KDQojIFRoZSB2YWx1ZXMgb2YgVklGIHNob3VsZCBiZSB3aXRoaW4gMi4gQW5kIGluIG5vIGNhc2UgaXQgc2hvdWxkIGJlIGdyZWF0ZXIgdGhhbiAxMC4gDQojIFNpbmNlIGFsbCB2YWx1ZXMgYXJlIGZyb20gMSB0byA0LCBhYnNlbmNlIG9mIG11bHRpY29sbGluZWFyaXR5IGlzIHdpdG5lc3NlZC4gDQpgYGANCmBgYHtyfQ0KDQojICBBZnRlciBjaGVja2luZyB0aGUgYXNzdW1wdGlvbiBvZiB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgd2UgY2FuIHNheSB0aGF0IHRoZSBhc3N1bXB0aW9ucyBzZWVtcyB0byBiZSBsYXJnZWx5IHNhdGlzZmllZC4NCg0KYGBgDQoNCg0KDQoNCiMjIyBBZGRpdGlvbmFsIERhdGEgdGhhdCBjb3VsZCBoYXZlIGJldHRlciBwcmVkaWN0ZWQgdGhlIG91dGNvbWUgdmFyaWFibGUNCg0KDQoxLkluIG9yZGVyIHRvIGJldHRlciBhbmFseXplIHRoZSBDTFYgb2YgYSBjdXN0b21lciwgdGhlIGNvbXBhbnkgbmVlZHMgdG8gbWFrZSBzdXJlIHRoZSBjdXN0b21lciBzdGF5cyB3aXRoIHRoZW0uIFRoaXMgY2FuIGJlIGNhbGN1bGF0ZWQgdXNpbmcgIkN1c3RvbWVyIFJldGVudGlvbiBSYXRlIiA6DQpDdXN0b21lciBSZXRlbnRpb24gUmF0ZSBpcyB0aGUgbnVtYmVyIG9mIGN1c3RvbWVycyByZXRhaW5lZCBieSBhIGNvbXBhbnkgb3ZlciBhIGNlcnRhaW4gdGltZSBwZXJpb2QuIEl04oCZcyBleHByZXNzZWQgYXMgYSBwZXJjZW50YWdlIG9mIGEgY29tcGFueeKAmXMgZXhpc3RpbmcgY3VzdG9tZXJzIHdobyByZW1haW4gDQpsb3lhbCB3aXRoaW4gdGhhdCB0aW1lIGZyYW1lLg0KIENSUiA9IFsoRS1OKS9TXSB4IDEwMCANCldoZXJlIDoNClRoZSBudW1iZXIgb2YgZXhpc3RpbmcgY3VzdG9tZXJzIGF0IHRoZSBzdGFydCBvZiB0aGUgdGltZSBwZXJpb2QgKFMpDQpUaGUgbnVtYmVyIG9mIHRvdGFsIGN1c3RvbWVycyBhdCB0aGUgZW5kIG9mIHRoZSB0aW1lIHBlcmlvZCAoRSkNClRoZSBudW1iZXIgb2YgbmV3IGN1c3RvbWVycyBhZGRlZCB3aXRoaW4gdGhlIHRpbWUgcGVyaW9kIChOKQ0KDQoyLklmIGEgY3VzdG9tZXIgaGFzIG1hZGUgbm8gY2xhaW1zIGZvciBhIHBlcmlvZCBvZiBuIHllYXJzLCBjb21wYW55IGNhbiBwcm92aWRlIHBlcmtzIHN1Y2ggYXMgIm5vIGNsYWltIGJvbnVzIiwgd2hpY2ggd291bGQgZXNzZW50aWFsbHkgcmVkdWNlIHRoZSBwcmVtaXVtIGFtb3VudCBwYXlhYmxlIGF0IG5leHQgcmVuZXdhbCwgDQp3aGlsZSBrZWVwaW5nIHRoZSBpbnN1cmFuY2UgY292ZXIgYXQgdGhlIHNhbWUgb3IgaGlnaGVyIHZhbHVlLiBXZSBoYXZlIHRoZSBkYXRhIHJlZ2FyZGluZyAiTW9udGhzIHNpbmNlIGxhc3QgY2xhaW0iICwgaG93ZXZlciBpZiB3ZSBoYXZlIGFuIGFkZGl0aW9uYWwgZGF0YSByZWdhcmRpbmcgY3VzdG9tZXJzIHJlc3BvbnNlIHRvIG9wdGluZyBmb3IgIm5vIGNsYWltIGJvbnVzIiBpdCB3b3VsZCBoZWxwIHVzIGFuYWx5emUgY3VzdG9tZXIgcHJlZmVyZW5jZSAuIEhlbmNlLCBsZWFkaW5nIHRvIGJldHRlciBwcmVkaWN0aW9uIG9mIGN1c3RvbWVycyB3aXRoIGNvbnNpc3RlbnQgQ0xWDQoNCjMuIElmIHRoZSBjb3N0IG9mIHN1c3RhaW5pbmcgU2FsZXMgQ2hhbm5lbCB3YXMgcHJvdmlkZWQgaXQgd291bGQgaGF2ZSBoZWxwZWQgdXMgYW5hbHl6ZSB0aGUgc3VzdGFpbmFiaWxpdHkgY29zdCBvZiBlYWNoIFNhbGVzIENoYW5uZWwsIHdpdGggd2hpY2ggd2UgY291bGQgaGF2ZSBlc3NlbnRpYWxseSBmb3VuZCBvdXQgd2hpY2ggU2FsZXMgQ2hhbm5lbCBpcyBjb250cmlidXRpbmcgZWZmZWN0aXZlbHkgdG8gdGhlIENMVi4gQmFzZWQgb24gdGhlIG91dGNvbWUsIHdlIGNhbiBzdWdnZXN0IGNvdXJzZXMgb2YgYWN0aW9uIHRvIHRoZSBjb21wYW55IHJlZHVjZSBzdWNoIG92ZXJoZWFkcy4NCg0KDQoNCiMjIyBDb250cmlidXRpb24gb2YgdGVhbSBtZW1iZXJzDQoNCg0KDQpUbyBjb29yZGluYXRlIHRoZSB0ZWFtIGFjdGl2aXRpZXMsIGZvcm1hbCBtZWV0aW5ncyB3ZXJlIGhlbGQgZXZlcnkgZGF5IGF0IDEyMDAgaHJzIGFuZCAxOTAwIGhycywgYmVzaWRlcyBpbmZvcm1hbCBtZWV0aW5ncyBvbiB0ZWFtcywgd2hhdHNhcHAgY2FsbHMsIHBob25lIGNhbGxzIGFuZCBpbm51bWVyYWJsZSBjaGF0cyBhdCBhbGwgaG91cnMgb2YgdGhlIGRheS4NCg0KSW5pdGlhbGx5IGV2ZXJ5Ym9keSBjYXJyaWVkIG91dCBFREEuIFRoZSByZXN1bHRzIG9mIEVEQSB3ZXJlIGRpc2N1c3NlZC4gRnVydGhlciBjb3Vyc2VzIG9mIGFjdGlvbiBvbiBFREEgd2VyZSBkaXNjdXNzZWQgYW5kIGNhcnJpZWQgb3V0LiBPbmNlIEVEQSB3YXMgc2F0aXNmYWN0b3JpbHkgZG9uZSwgZmVhdHVyZSBzZWxlY3Rpb24gYW5kIG1vZGVsIGJ1aWxkaW5nIHdhcyBjYXJyaWVkIG91dC4gSm9lbCwgQWxleCBhbmQgVGFzaGkgY2FycmllZCBvdXQgZmVhdHVyZSBzZWxlY3Rpb24gdXNpbmcgU3RlcHdpc2UgUmVncmVzc2lvbi4gU2FyYWgsIEFpc2h3YXJ5YSBhbmQgQmFydCBjYXJyaWVkIG91dCBGZWF0dXJlIHNlbGVjdGlvbiB1c2luZyBSYW5kb20gRm9yZXN0IGFuZCBBTk9WQS4gU2FyYWgsIEFpc2h3YXJ5YSBhbmQgQmFydCBjYXJyaWVkIG91dCBtb2RlbCBidWlsZGluZyB1c2luZyBSYW5kb20gRm9yZXN0IGltcGxlbWVudGluZyB2YXJpb3VzIG1vZGVscyB3aXRoIGFuZCB3aXRob3V0IHRyYW5zZm9ybWF0aW9uIGFzIGJyb3VnaHQgb3V0IGluIHRoZSByZXBvcnQgYWJvdmUuIEpvZWwsIEFsZXggYW5kIFRhc2hpIGNvbnN0cnVjdGVkIHZhcmlvdXMgbW9kZWxzIHVzaW5nIFJlZ3Jlc3Npb24sIHdpdGggYW5kIHdpdGhvdXQgdHJhbnNmb3JtYXRpb24gYXMgd2VsbCBhcywgd2l0aCBhbmQgd2l0aG91dCBzY2FsaW5nIGFzIGJyb3VnaHQgb3V0IGluIHRoZSByZXBvcnQgYWJvdmUuIFJldGVudGlvbiBhbmQgZGVsZXRpb24gb2Ygb3V0bGllcnMgYW5kIGVmZmVjdCBvZiBiaW5uaW5nIHdhcyBhbHNvIGV4cGVyaW1lbnRlZCB3aXRoLCBpbiB0aGUgbW9kZWxzIGJ5IGJvdGggc3ViLXRlYW1zLg0KDQpGaW5hbGx5LCBoYXZpbmcgY29tcGFyZWQgdGhlIHZhcmlvdXMgcmVzdWx0cyBhbmQgemVyb2luZyBvbiB0aGUgbW9zdCBvcGVyYXRpdmUgbW9kZWwgd2FzIGRvbmUsIHRoZSByZXBvcnQgd2FzIHByZXBhcmVkIGRyYXdpbmcgZnJvbSB0aGUgRURBIGFuZCBNb2RlbCBCdWlsZGluZyBkb25lIGVhcmxpZXIuIEFsbCBtZW1iZXJzIHNhdCB0b2dldGhlciBhbmQgY29uc3RydWN0ZWQgdGhlIHJlcG9ydCB2ZXR0aW5nIGFsbCBhc3BlY3RzIG9mIHRoZSByZXBvcnQgY29sbGVjdGl2ZWx5Lg0KDQpBbGwgbWVtYmVycyB3ZXJlIGludm9sdmVkIGF0IGV2ZXJ5IHN0YWdlIG9mIHRoZSBwcm9jZXNzIGZyb20gYmVnaW5uaW5nIHRvIGVuZC4NCg0KDQoNCiMjIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJlZmVyZW5jZXMNCg0KaHR0cHM6Ly93d3cuZ2Vla3Nmb3JnZWVrcy5vcmcvcmFuZG9tLWZvcmVzdC1hcHByb2FjaC1pbi1yLXByb2dyYW1taW5nLw0KDQpodHRwczovL2dpdGh1Yi5jb20vYWJoaXllcmFzaS9DTFYtQXV0by1JbnN1cmFuY2UNCg0KaHR0cDovL3d3dy5zdGhkYS5jb20vZW5nbGlzaC93aWtpL3R3by13YXktYW5vdmEtdGVzdC1pbi1yDQoNCmh0dHA6Ly93d3cuc3RoZGEuY29tL2VuZ2xpc2gvd2lraS9rcnVza2FsLXdhbGxpcy10ZXN0LWluLXINCg0KaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9qdWFuY2FybG9zdmVudG9zYS9tb2RlbHMtdG8taW1wcm92ZS1jdXN0b21lci1yZXRlbnRpb24NCg0KaHR0cHM6Ly9tZWRpdW0uY29tL0BhcmF2YW5zaGFkL2dyYWRpZW50LWJvb3N0aW5nLXZlcnN1cy1yYW5kb20tZm9yZXN0LWNmYTNmYThmMGQ4MA0KDQpodHRwczovL25lcHR1bmUuYWkvYmxvZy9yYW5kb20tZm9yZXN0LXJlZ3Jlc3Npb24td2hlbi1kb2VzLWl0LWZhaWwtYW5kLXdoeQ0KDQpodHRwczovL21lZGl1bS5jb20vQFRoZURhdGFHeWFuL2RheS04LWRhdGEtdHJhbnNmb3JtYXRpb24tc2tld25lc3Mtbm9ybWFsaXphdGlvbi1hbmQtbXVjaC1tb3JlLTRjMTQ0ZDM3MGU1NQ0KDQpodHRwczovL2Fuc2hpa2FheGVuYS5tZWRpdW0uY29tL2hvdy1za2V3ZWQtZGF0YS1jYW4tc2tyZXcteW91ci1saW5lYXItcmVncmVzc2lvbi1tb2RlbC1hY2N1cmFjeS1hbmQtdHJhbnNmcm9tYXRpb24tY2FuLWhlbHAtNjJjNmQzZmU0YzUzDQoNCg0KaHR0cDovL3Itc3RhdGlzdGljcy5jby9WYXJpYWJsZS1TZWxlY3Rpb24tYW5kLUltcG9ydGFuY2UtV2l0aC1SLmh0bWwNCg0KaHR0cHM6Ly9kYXRhYXNwaXJhbnQuY29tL2ZlYXR1cmUtc2VsZWN0aW9uLXRlY2huaXF1ZXMtci8NCg0KDQoNCg0KDQoNCg0KDQoNCg0K